/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

/**
 * @namespace Oryx name space for different utility methods
 * @name ORYX.Utils
 */

if (!ORYX) var ORYX = {}

ORYX.Utils = {
  /**
   * General helper method for parsing a param out of current location url
   * @example
   * // Current url in Browser => "http://oryx.org?param=value"
   * ORYX.Utils.getParamFromUrl("param") // => "value"
   * @param {Object} name
   */
  getParamFromUrl: function(name) {
    name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]')
    var regexS = '[\\?&]' + name + '=([^&#]*)'
    var regex = new RegExp(regexS)
    var results = regex.exec(window.location.href)
    if (results == null) {
      return null
    } else {
      return results[1]
    }
  },

  adjustLightness: function() {
    return arguments[0]
  },

  adjustGradient: function(gradient, reference) {
    if (ORYX.CONFIG.DISABLE_GRADIENT && gradient) {
      var col = reference.getAttributeNS(null, 'stop-color') || '#ffffff'

      $A(gradient.getElementsByTagName('stop')).each(function(stop) {
        if (stop == reference) {
          return
        }
        stop.setAttributeNS(null, 'stop-color', col)
      })
    }
  }
}
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

XMLNS = {
  ATOM: 'http://www.w3.org/2005/Atom',
  XHTML: 'http://www.w3.org/1999/xhtml',
  ERDF: 'http://purl.org/NET/erdf/profile',
  RDFS: 'http://www.w3.org/2000/01/rdf-schema#',
  RDF: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
  RAZIEL: 'http://b3mn.org/Raziel',

  SCHEMA: ''
}

//TODO kann kickstart sich vielleicht auch um die erzeugung von paketen/
// namespaces k�mmern? z.b. requireNamespace("ORYX.Core.SVG");
var Kickstart = {
  started: false,
  callbacks: [],
  alreadyLoaded: [],
  PATH: '',

  load: function() {
    Kickstart.kick()
  },

  kick: function() {
    //console.profile("loading");
    if (!Kickstart.started) {
      Kickstart.started = true
      Kickstart.callbacks.each(function(callback) {
        // call the registered callback asynchronously.
        window.setTimeout(callback, 1)
      })
    }
  },

  register: function(callback) {
    //TODO Add some mutual exclusion between kick and register calls.
    with (Kickstart) {
      if (started) window.setTimeout(callback, 1)
      else Kickstart.callbacks.push(callback)
    }
  },

  /**
   * Loads a js, assuring that it has only been downloaded once.
   * @param {String} url the script to load.
   */
  require: function(url) {
    // if not already loaded, include it.
    if (Kickstart.alreadyLoaded.member(url)) return false
    return Kickstart.include(url)
  },

  /**
   * Loads a js, regardless of whether it has only been already downloaded.
   * @param {String} url the script to load.
   */
  include: function(url) {
    // prepare a script tag and place it in html head.
    var head = document.getElementsByTagNameNS(XMLNS.XHTML, 'head')[0]
    var s = document.createElementNS(XMLNS.XHTML, 'script')
    s.setAttributeNS(XMLNS.XHTML, 'type', 'text/javascript')
    s.src = Kickstart.PATH + url

    //TODO macht es sinn, dass neue skript als letztes kind in den head
    // einzubinden (stichwort reihenfolge der skript tags)?
    head.appendChild(s)

    // remember this url.
    Kickstart.alreadyLoaded.push(url)

    return true
  }
}

// register kickstart as the new onload event listener on current window.
// previous listener(s) are triggered to launch with kickstart.
Event.observe(window, 'load', Kickstart.load)
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

var ERDF = {
  LITERAL: 0x01,
  RESOURCE: 0x02,
  DELIMITERS: ['.', '-'],
  HASH: '#',
  HYPHEN: '-',

  schemas: [],
  callback: undefined,
  log: undefined,

  init: function(callback) {
    // init logging.
    //ERDF.log = Log4js.getLogger("oryx");
    //ERDF.log.setLevel(Log4js.Level.ALL);
    //ERDF.log.addAppender(new ConsoleAppender(ERDF.log, false));

    //if(ERDF.log.isTraceEnabled())
    //	ERDF.log.trace("ERDF Parser is initialized.");

    // register callbacks and default schemas.
    ERDF.callback = callback
    ERDF.registerSchema('schema', XMLNS.SCHEMA)
    ERDF.registerSchema('rdfs', XMLNS.RDFS)
  },

  run: function() {
    //if(ERDF.log.isTraceEnabled())
    //	ERDF.log.trace("ERDF Parser is running.");

    // do the work.
    return ERDF._checkProfile() && ERDF.parse()
  },

  parse: function() {
    //(ERDF.log.isDebugEnabled())
    //	ERDF.log.debug("Begin parsing document metadata.");

    // time measuring
    ERDF.__startTime = new Date()

    var bodies = document.getElementsByTagNameNS(XMLNS.XHTML, 'body')
    var subject = { type: ERDF.RESOURCE, value: '' }

    var result = ERDF._parseDocumentMetadata() && ERDF._parseFromTag(bodies[0], subject)

    // time measuring
    ERDF.__stopTime = new Date()

    var duration = (ERDF.__stopTime - ERDF.__startTime) / 1000
    //alert('ERDF parsing took ' + duration + ' s.');

    return result
  },

  _parseDocumentMetadata: function() {
    // get links from head element.
    var heads = document.getElementsByTagNameNS(XMLNS.XHTML, 'head')
    var links = heads[0].getElementsByTagNameNS(XMLNS.XHTML, 'link')
    var metas = heads[0].getElementsByTagNameNS(XMLNS.XHTML, 'meta')

    // process links first, since they could contain schema definitions.
    $A(links).each(function(link) {
      var properties = link.getAttribute('rel')
      var reversedProperties = link.getAttribute('rev')
      var value = link.getAttribute('href')

      ERDF._parseTriplesFrom(ERDF.RESOURCE, '', properties, ERDF.RESOURCE, value)

      ERDF._parseTriplesFrom(ERDF.RESOURCE, value, reversedProperties, ERDF.RESOURCE, '')
    })

    // continue with metas.
    $A(metas).each(function(meta) {
      var property = meta.getAttribute('name')
      var value = meta.getAttribute('content')

      ERDF._parseTriplesFrom(ERDF.RESOURCE, '', property, ERDF.LITERAL, value)
    })

    return true
  },

  _parseFromTag: function(node, subject, depth) {
    // avoid parsing non-xhtml content.
    if (!node || !node.namespaceURI || node.namespaceURI != XMLNS.XHTML) {
      return
    }

    // housekeeping.
    if (!depth) depth = 0
    var id = node.getAttribute('id')

    // some logging.
    //if(ERDF.log.isTraceEnabled())
    //	ERDF.log.trace(">".times(depth) + " Parsing " + node.nodeName + " ("+node.nodeType+") for data on " +
    //		((subject.type == ERDF.RESOURCE) ? ('&lt;' + subject.value + '&gt;') : '') +
    //		((subject.type == ERDF.LITERAL) ? '"' + subject.value + '"' : ''));

    /* triple finding! */

    // in a-tags...
    if (node.nodeName.endsWith(':a') || node.nodeName == 'a') {
      var properties = node.getAttribute('rel')
      var reversedProperties = node.getAttribute('rev')
      var value = node.getAttribute('href')
      var title = node.getAttribute('title')
      var content = node.textContent

      // rel triples
      ERDF._parseTriplesFrom(subject.type, subject.value, properties, ERDF.RESOURCE, value, function(triple) {
        var label = title ? title : content

        // label triples
        ERDF._parseTriplesFrom(triple.object.type, triple.object.value, 'rdfs.label', ERDF.LITERAL, label)
      })

      // rev triples
      ERDF._parseTriplesFrom(subject.type, subject.value, reversedProperties, ERDF.RESOURCE, '')

      // type triples
      ERDF._parseTypeTriplesFrom(subject.type, subject.value, properties)

      // in img-tags...
    } else if (node.nodeName.endsWith(':img') || node.nodeName == 'img') {
      var properties = node.getAttribute('class')
      var value = node.getAttribute('src')
      var alt = node.getAttribute('alt')

      ERDF._parseTriplesFrom(subject.type, subject.value, properties, ERDF.RESOURCE, value, function(triple) {
        var label = alt

        // label triples
        ERDF._parseTriplesFrom(triple.object.type, triple.object.value, 'rdfs.label', ERDF.LITERAL, label)
      })
    }

    // in every tag
    var properties = node.getAttribute('class')
    var title = node.getAttribute('title')
    var content = node.textContent
    var label = title ? title : content

    // regular triples
    ERDF._parseTriplesFrom(subject.type, subject.value, properties, ERDF.LITERAL, label)

    if (id) subject = { type: ERDF.RESOURCE, value: ERDF.HASH + id }

    // type triples
    ERDF._parseTypeTriplesFrom(subject.type, subject.value, properties)

    // parse all children that are element nodes.
    var children = node.childNodes
    if (children)
      $A(children).each(function(_node) {
        if (_node.nodeType == _node.ELEMENT_NODE) ERDF._parseFromTag(_node, subject, depth + 1)
      })
  },

  _parseTriplesFrom: function(subjectType, subject, properties, objectType, object, callback) {
    if (!properties) return
    properties
      .toLowerCase()
      .split(' ')
      .each(function(property) {
        //if(ERDF.log.isTraceEnabled())
        //	ERDF.log.trace("Going for property " + property);

        var schema = ERDF.schemas.find(function(schema) {
          return (
            false ||
            ERDF.DELIMITERS.find(function(delimiter) {
              return property.startsWith(schema.prefix + delimiter)
            })
          )
        })

        if (schema && object) {
          property = property.substring(schema.prefix.length + 1, property.length)
          var triple = ERDF.registerTriple(
            new ERDF.Resource(subject),
            { prefix: schema.prefix, name: property },
            objectType == ERDF.RESOURCE ? new ERDF.Resource(object) : new ERDF.Literal(object)
          )

          if (callback) callback(triple)
        }
      })
  },

  _parseTypeTriplesFrom: function(subjectType, subject, properties, callback) {
    if (!properties) return
    properties
      .toLowerCase()
      .split(' ')
      .each(function(property) {
        //if(ERDF.log.isTraceEnabled())
        //	ERDF.log.trace("Going for property " + property);

        var schema = ERDF.schemas.find(function(schema) {
          return (
            false ||
            ERDF.DELIMITERS.find(function(delimiter) {
              return property.startsWith(ERDF.HYPHEN + schema.prefix + delimiter)
            })
          )
        })

        if (schema && subject) {
          property = property.substring(schema.prefix.length + 2, property.length)
          var triple = ERDF.registerTriple(
            subjectType == ERDF.RESOURCE ? new ERDF.Resource(subject) : new ERDF.Literal(subject),
            { prefix: 'rdf', name: 'type' },
            new ERDF.Resource(schema.namespace + property)
          )
          if (callback) callback(triple)
        }
      })
  },

  /**
   * Checks for ERDF profile declaration in head of document.
   */
  _checkProfile: function() {
    // get profiles from head element.
    var heads = document.getElementsByTagNameNS(XMLNS.XHTML, 'head')
    var profiles = heads[0].getAttribute('profile')
    var found = false

    // if erdf profile is contained.
    if (profiles && profiles.split(' ').member(XMLNS.ERDF)) {
      // pass check.
      //if(ERDF.log.isTraceEnabled())
      //	ERDF.log.trace("Found ERDF profile " + XMLNS.ERDF);
      return true
    } else {
      // otherwise fail check.
      //if(ERDF.log.isFatalEnabled())
      //	ERDF.log.fatal("No ERDF profile found.");
      return false
    }
  },

  __stripHashes: function(s) {
    return s && typeof s.substring == 'function' && s.substring(0, 1) == '#' ? s.substring(1, s.length) : s
  },

  registerSchema: function(prefix, namespace) {
    // TODO check whether already registered, if so, complain.
    ERDF.schemas.push({
      prefix: prefix,
      namespace: namespace
    })

    //if(ERDF.log.isDebugEnabled())
    //	ERDF.log.debug("Prefix '"+prefix+"' for '"+namespace+"' registered.");
  },

  registerTriple: function(subject, predicate, object) {
    // if prefix is schema, this is a schema definition.
    if (predicate.prefix.toLowerCase() == 'schema') this.registerSchema(predicate.name, object.value)

    var triple = new ERDF.Triple(subject, predicate, object)
    ERDF.callback(triple)

    //if(ERDF.log.isInfoEnabled())
    //	ERDF.log.info(triple)

    // return the registered triple.
    return triple
  },

  __enhanceObject: function() {
    /* Resource state querying methods */
    this.isResource = function() {
      return this.type == ERDF.RESOURCE
    }
    this.isLocal = function() {
      return this.isResource() && this.value.startsWith('#')
    }
    this.isCurrentDocument = function() {
      return this.isResource() && this.value == ''
    }

    /* Resource getter methods.*/
    this.getId = function() {
      return this.isLocal() ? ERDF.__stripHashes(this.value) : false
    }

    /* Liiteral state querying methods  */
    this.isLiteral = function() {
      return this.type == ERDF.LIITERAL
    }
  },

  serialize: function(literal) {
    if (!literal) {
      return ''
    } else if (literal.constructor == String) {
      return literal
    } else if (literal.constructor == Boolean) {
      return literal ? 'true' : 'false'
    } else {
      return literal.toString()
    }
  }
}

ERDF.Triple = function(subject, predicate, object) {
  this.subject = subject
  this.predicate = predicate
  this.object = object

  this.toString = function() {
    return (
      '[ERDF.Triple] ' +
      this.subject.toString() +
      ' ' +
      this.predicate.prefix +
      ':' +
      this.predicate.name +
      ' ' +
      this.object.toString()
    )
  }
}

ERDF.Resource = function(uri) {
  this.type = ERDF.RESOURCE
  this.value = uri
  ERDF.__enhanceObject.apply(this)

  this.toString = function() {
    return '&lt;' + this.value + '&gt;'
  }
}

ERDF.Literal = function(literal) {
  this.type = ERDF.LITERAL
  this.value = ERDF.serialize(literal)
  ERDF.__enhanceObject.apply(this)

  this.toString = function() {
    return '"' + this.value + '"'
  }
}
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

/*
 * Save and triple generation behaviour. Use this area to configure
 * data management to your needs.
 */
var USE_ASYNCHRONOUS_REQUESTS = true
var DISCARD_UNUSED_TRIPLES = true
var PREFER_SPANS_OVER_DIVS = true
var PREFER_TITLE_OVER_TEXTNODE = false
var RESOURCE_ID_PREFIX = 'resource'

var SHOW_DEBUG_ALERTS_WHEN_SAVING = false
var SHOW_EXTENDED_DEBUG_INFORMATION = false

/*
 * Back end specific workarounds.
 */

var USE_ARESS_WORKAROUNDS = true

/*
 * Data management constants. Do not change these, as they are used
 * both internally and externally to communicate on events and to identify
 * command object actions in triple production and embedding rules.
 */

// Resource constants
var RESOURCE_CREATED = 0x01
var RESOURCE_REMOVED = 0x02
var RESOURCE_SAVED = 0x04
var RESOURCE_RELOADED = 0x08
var RESOURCE_SYNCHRONIZED = 0x10

// Triple constants
var TRIPLE_REMOVE = 0x01
var TRIPLE_ADD = 0x02
var TRIPLE_RELOAD = 0x04
var TRIPLE_SAVE = 0x08

var PROCESSDATA_REF = 'processdata'

// HTTP status code constants
//
//// 2xx
//const 200_OK =			'Ok';
//const 201_CREATED =		'Created';
//const 202_ACCEPTED =		'Accepted';
//const 204_NO_CONTENT =	'No Content';
//
//// 3xx
//const 301_MOVED_PERMANENTLY =	'Moved Permanently';
//const 302_MOVED_TEMPORARILY =	'Moved Temporarily';
//const 304_NOT_MODIFIED =		'Not Modified';
//
//// 4xx
//const 400_BAD_REQUEST =	'Bad Request';
//const 401_UNAUTHORIZED =	'Unauthorized';
//const 403_FORBIDDEN =		'Forbidden';
//const 404_NOT_FOUND =		'Not Found';
//const 409_CONFLICT =		'Conflict';
//
//// 5xx
//const 500_INTERNAL_SERVER_ERROR =		'Internal Server Error';
//const 501_NOT_IMPLEMENTED =			'Not Implemented';
//const 502_BAD_GATEWAY =				'Bad Gateway';
//const 503_SERVICE_UNAVAILABLE =		'Service Unavailable';
//
/**
 * The Data Management object. Use this one when interacting with page internal
 * data. Initialize data management by DataManager.init();
 * @class DataManager
 */
var DataManager = {
  /**
   * The init method should be called once in the DataManagers lifetime.
   * It causes the DataManager to initialize itself, the erdf parser, do all
   * neccessary registrations and configurations, to run the parser and
   * from then on deliver all resulting triples.
   * No parameters needed are needed in a call to this method.
   */
  init: function() {
    ERDF.init(DataManager._registerTriple)
    DataManager.__synclocal()
  },

  /**
   * This triple array is meant to be the whole knowledge of the DataManager.
   */
  _triples: [],

  /**
   * This method is meant for callback from erdf parsing. It is not to be
   * used in another way than to add triples to the triple store.
   * @param {Object} triple the triple to add to the triple store.
   */
  _registerTriple: function(triple) {
    DataManager._triples.push(triple)
  },

  /**
   * The __synclocal method is for internal usage only.
   * It performs synchronization with the local document, that is, the triple
   * store is adjustet to the content of the document, which could have been
   * changed by any other applications running on the same page.
   */
  __synclocal: function() {
    DataManager._triples = []
    ERDF.run()
  },

  /**
   * Makes the shape passed into this method synchronize itself with the DOM.
   * This method returns the shapes resource object for further manipulation.
   * @param {Object} shape
   */
  __synchronizeShape: function(shape) {
    var r = ResourceManager.getResource(shape.resourceId)
    var serialize = shape.serialize()

    // store all serialize values
    serialize.each(function(ser) {
      var resource = ser.type == 'resource'
      var _triple = new ERDF.Triple(
        new ERDF.Resource(shape.resourceId),
        { prefix: ser.prefix, name: ser.name },
        resource ? new ERDF.Resource(ser.value) : new ERDF.Literal(ser.value)
      )
      DataManager.setObject(_triple)
    })

    return r
  },

  __storeShape: function(shape) {
    // first synchronize the shape,
    var resource = DataManager.__synchronizeShape(shape)

    // then save the synchronized dom.
    resource.save()
  },

  __forceExistance: function(shape) {
    if (!$(shape.resourceId)) {
      if (!$$('.' + PROCESSDATA_REF)[0])
        DataManager.graft(XMLNS.XHTML, document.getElementsByTagNameNS(XMLNS.XHTML, 'body').item(0), [
          'div',
          { class: PROCESSDATA_REF, style: 'display:none;' }
        ])

      // object is literal
      DataManager.graft(XMLNS.XHTML, $$('.' + PROCESSDATA_REF)[0], [
        'div',
        {
          id: shape.resourceId,
          //This should be done in a more dynamic way!!!!!
          class: shape instanceof ORYX.Core.Canvas ? '-oryx-canvas' : undefined
        }
      ])
    } else {
      var resource = $(shape.resourceId)
      var children = $A(resource.childNodes)
      children.each(function(child) {
        resource.removeChild(child)
      })
    }
  },

  __persistShape: function(shape) {
    // a shape serialization.
    var shapeData = shape.serialize()

    // initialize a triple array and construct a shape resource
    // to be used in triple generation.
    var triplesArray = []
    var shapeResource = new ERDF.Resource(shape.resourceId)

    // remove all triples for this particular shape's resource
    DataManager.removeTriples(DataManager.query(shapeResource, undefined, undefined))

    // for each data set in the shape's serialization
    shapeData.each(function(data) {
      // construct a triple's value
      var value = data.type == 'resource' ? new ERDF.Resource(data.value) : new ERDF.Literal(data.value)

      // construct triple and add it to the DOM.
      DataManager.addTriple(new ERDF.Triple(shapeResource, { prefix: data.prefix, name: data.name }, value))
    })
  },

  __persistDOM: function(facade) {
    // getChildShapes gets all shapes (nodes AND edges), deep flag
    // makes it return a flattened child hierarchy.

    var canvas = facade.getCanvas()
    var shapes = canvas.getChildShapes(true)
    var result = ''

    // persist all shapes.
    shapes.each(function(shape) {
      DataManager.__forceExistance(shape)
    })
    //DataManager.__synclocal();

    DataManager.__renderCanvas(facade)
    result += DataManager.serialize($(ERDF.__stripHashes(facade.getCanvas().resourceId)), true)

    shapes.each(function(shape) {
      DataManager.__persistShape(shape)
      result += DataManager.serialize($(ERDF.__stripHashes(shape.resourceId)), true)
    })

    //result += DataManager.__renderCanvas(facade);

    return result
  },

  __renderCanvas: function(facade) {
    var canvas = facade.getCanvas()
    var stencilSets = facade.getStencilSets()
    var shapes = canvas.getChildShapes(true)

    DataManager.__forceExistance(canvas)

    DataManager.__persistShape(canvas)

    var shapeResource = new ERDF.Resource(canvas.resourceId)

    // remove all triples for this particular shape's resource
    DataManager.removeTriples(DataManager.query(shapeResource, undefined, undefined))

    DataManager.addTriple(
      new ERDF.Triple(shapeResource, { prefix: 'oryx', name: 'mode' }, new ERDF.Literal('writable'))
    )

    DataManager.addTriple(
      new ERDF.Triple(shapeResource, { prefix: 'oryx', name: 'mode' }, new ERDF.Literal('fullscreen'))
    )

    stencilSets.values().each(function(stencilset) {
      DataManager.addTriple(
        new ERDF.Triple(
          shapeResource,
          { prefix: 'oryx', name: 'stencilset' },
          new ERDF.Resource(stencilset.source().replace(/&/g, '%26'))
        )
      )

      DataManager.addTriple(
        new ERDF.Triple(
          shapeResource,
          { prefix: 'oryx', name: 'ssnamespace' },
          new ERDF.Resource(stencilset.namespace())
        )
      )

      stencilset
        .extensions()
        .keys()
        .each(function(extension) {
          DataManager.addTriple(
            new ERDF.Triple(shapeResource, { prefix: 'oryx', name: 'ssextension' }, new ERDF.Literal(extension))
          )
        })
    })

    shapes.each(function(shape) {
      DataManager.addTriple(
        new ERDF.Triple(shapeResource, { prefix: 'oryx', name: 'render' }, new ERDF.Resource('#' + shape.resourceId))
      )
    })
  },

  __counter: 0,
  __provideId: function() {
    while ($(RESOURCE_ID_PREFIX + DataManager.__counter)) DataManager.__counter++

    return RESOURCE_ID_PREFIX + DataManager.__counter
  },

  serializeDOM: function(facade) {
    return DataManager.__persistDOM(facade)
  },

  syncGlobal: function(facade) {
    return DataManager.__syncglobal(facade)
  },

  /**
   * This method is used to synchronize local DOM with remote resources.
   * Local changes are commited to the server, and remote changes are
   * performed to the local document.
   * @param {Object} facade The facade of the editor that holds certain
   * resource representations as shapes.
   */
  __syncglobal: function(facade) {
    // getChildShapes gets all shapes (nodes AND edges), deep flag
    // makes it return a flattened child hierarchy.

    var canvas = facade.getCanvas()
    var shapes = canvas.getChildShapes(true)

    // create dummy resource representations in the dom
    // for all shapes that were newly created.

    shapes
      .select(function(shape) {
        // select shapes without resource id.

        return !$(shape.resourceId)
      })
      .each(function(shape) {
        // create new resources for them.
        if (USE_ARESS_WORKAROUNDS) {
          /*
           * This is a workaround due to a bug in aress. Resources are
           * ignoring changes to raziel:type property once they are
           * created. As long as this is not fixed, the resource is now
           * being created using a randomly guessed id, this temporary id
           * is then used in references and the appropriate div is being
           * populated with properties.
           *
           * AFTER THIS PHASE THE DATA IS INCONSISTENT AS REFERENCES POINT
           * TO IDS THAT ARE UNKNOWN TO THE BACK END.
           *
           * After the resource is actually created in aress, it gets an id
           * that is persistent. All shapes are then being populated with the
           * correct id references and stored on the server.
           *
           * AFTER THE SAVE PROCESS HAS RETURNED, THE DATA IS CONSISTENT
           * REGARDING THE ID REFERENCES AGAIN.
           */

          var razielType = shape.properties['raziel-type']

          var div =
            '<div xmlns="http://www.w3.org/1999/xhtml">' + '<span class="raziel-type">' + razielType + '</span></div>'

          var r = ResourceManager.__createResource(div)
          shape.resourceId = r.id()
        } else {
          var r = ResourceManager.__createResource()
          shape.resourceId = r.id()
        }
      })

    shapes.each(function(shape) {
      // store all shapes.
      DataManager.__storeShape(shape)
    })
  },

  /**
   * This method serializes a single div into a string that satisfies the
   * client/server communication protocol. It ingnores all elements that have
   * an attribute named class that includes 'transient'.
   * @param {Object} node the element to serialize.
   * @param {Object} preserveNamespace whether to preserve the parent's
   *                 namespace. If you are not sure about namespaces, provide
   *                 just the element to be serialized.
   */
  serialize: function(node, preserveNamespace) {
    if (node.nodeType == node.ELEMENT_NODE) {
      // serialize an element node.

      var children = $A(node.childNodes)
      var attributes = $A(node.attributes)
      var clazz = new String(node.getAttribute('class'))
      var ignore = clazz.split(' ').member('transient')

      // ignore transients.

      if (ignore) return ''

      // start serialization.

      var result = '<' + node.nodeName

      // preserve namespace?
      if (!preserveNamespace)
        result +=
          ' xmlns="' + (node.namespaceURI ? node.namespaceURI : XMLNS.XHTML) + '" xmlns:oryx="http://oryx-editor.org"'

      // add all attributes.

      attributes.each(function(attribute) {
        result += ' ' + attribute.nodeName + '="' + attribute.nodeValue + '"'
      })

      // close if no children.

      if (children.length == 0) result += '/>'
      else {
        // serialize all children.

        result += '>'
        children.each(function(_node) {
          result += DataManager.serialize(_node, true)
        })
        result += '</' + node.nodeName + '>'
      }

      return result
    } else if (node.nodeType == node.TEXT_NODE) {
      // serialize a text node.
      return node.nodeValue
    }

    //TODO serialize cdata areas also.
    //TODO work on namespace awareness.
  },

  addTriple: function(triple) {
    // assert the subject is a resource

    if (!triple.subject.type == ERDF.LITERAL)
      throw 'Cannot add the triple ' + triple.toString() + ' because the subject is not a resource.'

    // get the element which represents this triple's subject.
    var elementId = ERDF.__stripHashes(triple.subject.value)
    var element = $(elementId)

    // assert the subject is inside this document.
    if (!element)
      throw 'Cannot add the triple ' +
        triple.toString() +
        ' because the subject "' +
        elementId +
        '" is not in the document.'

    if (triple.object.type == ERDF.LITERAL)
      // object is literal
      DataManager.graft(XMLNS.XHTML, element, [
        'span',
        { class: triple.predicate.prefix + '-' + triple.predicate.name },
        triple.object.value.escapeHTML()
      ])
    else {
      // object is resource
      DataManager.graft(XMLNS.XHTML, element, [
        'a',
        { rel: triple.predicate.prefix + '-' + triple.predicate.name, href: triple.object.value }
      ])
    }

    return true
  },

  removeTriples: function(triples) {
    // alert('Removing ' +triples.length+' triples.');

    // from all the triples select those ...
    var removed = triples.select(function(triple) {
      // TODO remove also from triple store.
      // ... that were actually removed.
      return DataManager.__removeTriple(triple)
    })

    // sync and return removed triples.
    // DataManager.__synclocal();
    return removed
  },

  removeTriple: function(triple) {
    // remember whether the triple was actually removed.
    var result = DataManager.__removeTriple(triple)

    // sync and return removed triples.
    // DataManager.__synclocal();
    return result
  },

  __removeTriple: function(triple) {
    // assert the subject is a resource
    if (!triple.subject.type == ERDF.LITERAL)
      throw 'Cannot remove the triple ' + triple.toString() + ' because the subject is not a resource.'

    // get the element which represents this triple's subject.
    var elementId = ERDF.__stripHashes(triple.subject.value)
    var element = $(elementId)

    // assert the subject is inside this document.
    if (!element) throw 'Cannot remove the triple ' + triple.toString() + ' because the subject is not in the document.'

    if (triple.object.type == ERDF.LITERAL) {
      // continue searching actively for the triple.
      var result = DataManager.__removeTripleRecursively(triple, element)
      return result
    }
  },

  __removeTripleRecursively: function(triple, continueFrom) {
    // return when this node is not an element node.
    if (continueFrom.nodeType != continueFrom.ELEMENT_NODE) return false

    var classes = new String(continueFrom.getAttribute('class'))
    var children = $A(continueFrom.childNodes)

    if (classes.include(triple.predicate.prefix + '-' + triple.predicate.name)) {
      var content = continueFrom.textContent
      if (triple.object.type == ERDF.LITERAL && triple.object.value == content)
        continueFrom.parentNode.removeChild(continueFrom)

      return true
    } else {
      children.each(function(_node) {
        DataManager.__removeTripleRecursively(triple, _node)
      })
      return false
    }
  },

  /**
   * graft() function
   * Originally by Sean M. Burke from interglacial.com, altered for usage with
   * SVG and namespace (xmlns) support. Be sure you understand xmlns before
   * using this funtion, as it creates all grafted elements in the xmlns
   * provided by you and all element's attribures in default xmlns. If you
   * need to graft elements in a certain xmlns and wish to assign attributes
   * in both that and another xmlns, you will need to do stepwise grafting,
   * adding non-default attributes yourself or you'll have to enhance this
   * function. Latter, I would appreciate: martin�apfelfabrik.de
   * @param {Object} namespace The namespace in which
   * 					elements should be grafted.
   * @param {Object} parent The element that should contain the grafted
   * 					structure after the function returned.
   * @param {Object} t the crafting structure.
   * @param {Object} doc the document in which grafting is performed.
   */
  graft: function(namespace, parent, t, doc) {
    doc = doc || (parent && parent.ownerDocument) || document
    var e
    if (t === undefined) {
      echo("Can't graft an undefined value")
    } else if (t.constructor == String) {
      e = doc.createTextNode(t)
    } else {
      for (var i = 0; i < t.length; i++) {
        if (i === 0 && t[i].constructor == String) {
          var snared = t[i].match(/^([a-z][a-z0-9]*)\.([^\s\.]+)$/i)
          if (snared) {
            e = doc.createElementNS(namespace, snared[1])
            e.setAttributeNS(null, 'class', snared[2])
            continue
          }
          snared = t[i].match(/^([a-z][a-z0-9]*)$/i)
          if (snared) {
            e = doc.createElementNS(namespace, snared[1]) // but no class
            continue
          }

          // Otherwise:
          e = doc.createElementNS(namespace, 'span')
          e.setAttribute(null, 'class', 'namelessFromLOL')
        }

        if (t[i] === undefined) {
          echo("Can't graft an undefined value in a list!")
        } else if (t[i].constructor == String || t[i].constructor == Array) {
          this.graft(namespace, e, t[i], doc)
        } else if (t[i].constructor == Number) {
          this.graft(namespace, e, t[i].toString(), doc)
        } else if (t[i].constructor == Object) {
          // hash's properties => element's attributes
          for (var k in t[i]) {
            e.setAttributeNS(null, k, t[i][k])
          }
        } else if (t[i].constructor == Boolean) {
          this.graft(namespace, e, t[i] ? 'true' : 'false', doc)
        } else throw 'Object ' + t[i] + ' is inscrutable as an graft arglet.'
      }
    }

    if (parent) parent.appendChild(e)

    return Element.extend(e) // return the topmost created node
  },

  setObject: function(triple) {
    /**
     * Erwartungen von Arvid an diese Funktion:
     * - Es existiert genau ein triple mit dem Subjekt und Praedikat,
     *   das uebergeben wurde, und dieses haelt uebergebenes Objekt.
     */

    var triples = DataManager.query(triple.subject, triple.predicate, undefined)

    DataManager.removeTriples(triples)

    DataManager.addTriple(triple)

    return true
  },

  query: function(subject, predicate, object) {
    /*
     * Typical triple.
     *	{value: subject, type: subjectType},
     *	{prefix: schema.prefix, name: property},
     *	{value: object, type: objectType});
     */

    return DataManager._triples.select(function(triple) {
      var select = subject ? triple.subject.type == subject.type && triple.subject.value == subject.value : true
      if (predicate) {
        select = select && (predicate.prefix ? triple.predicate.prefix == predicate.prefix : true)
        select = select && (predicate.name ? triple.predicate.name == predicate.name : true)
      }
      select = select && (object ? triple.object.type == object.type && triple.object.value == object.value : true)
      return select
    })
  }
}

Kickstart.register(DataManager.init)

function assert(expr, m) {
  if (!expr) throw m
}

function DMCommand(action, triple) {
  // store action and triple.
  this.action = action
  this.triple = triple

  this.toString = function() {
    return 'Command(' + action + ', ' + triple + ')'
  }
}

function DMCommandHandler(nextHandler) {
  /**
   * Private method to set the next handler in the Chain of Responsibility
   * (see http://en.wikipedia.org/wiki/Chain-of-responsibility_pattern for
   * details).
   * @param {DMCommandHandler} handler The handler that is next in the chain.
   */
  this.__setNext = function(handler) {
    var _next = this.__next
    this.__next = nextHandler
    return _next ? _next : true
  }
  this.__setNext(nextHandler)

  /**
   * Invokes the next handler. If there is no next handler, this method
   * returns false, otherwise it forwards the result of the handling.
   * @param {Object} command The command object to be processed.
   */
  this.__invokeNext = function(command) {
    return this.__next ? this.__next.handle(command) : false
  }

  /**
   * Handles a command. The abstract method process() is called with the
   * command object that has been passed. If the process method catches the
   * command (returns true on completion), the handle() method returns true.
   * If the process() method doesn't catch the command, the next handler will
   * be invoked.
   * @param {Object} command The command object to be processed.
   */
  this.handle = function(command) {
    return this.process(command) ? true : this.__invokeNext(command)
  }

  /**
   * Empty process() method returning false. If javascript knew abstract
   * class members, this would be one.
   * @param {Object} command The command object to process.
   */
  this.process = function(command) {
    return false
  }
}

/**
 * This Handler manages the addition and the removal of meta elements in the
 * head of the document.
 * @param {DMCommandHandler} next The handler that is next in the chain.
 */
function MetaTagHandler(next) {
  DMCommandHandler.apply(this, [next])
  this.process = function(command) {
    with (command.triple) {
      /* assert prerequisites */
      if (!(subject instanceof ERDF.Resource && subject.isCurrentDocument() && object instanceof ERDF.Literal))
        return false
    }
  }
}

var chain = new MetaTagHandler()
var command = new DMCommand(TRIPLE_ADD, new ERDF.Triple(new ERDF.Resource(''), 'rdf:tool', new ERDF.Literal('')))

/*
if(chain.handle(command))
	alert('Handled!');
*/

ResourceManager = {
  __corrupt: false,
  __latelyCreatedResource: undefined,
  __listeners: $H(),
  __token: 1,

  addListener: function(listener, mask) {
    if (!(listener instanceof Function)) throw 'Resource event listener is not a function!'
    if (!mask) throw 'Invalid mask for resource event listener registration.'

    // construct controller and token.
    var controller = { listener: listener, mask: mask }
    var token = ResourceManager.__token++

    // add new listener.
    ResourceManager.__listeners[token] = controller

    // return the token generated.
    return token
  },

  removeListener: function(token) {
    // remove the listener with the token and return it.
    return ResourceManager.__listners.remove(token)
  },

  __Event: function(action, resourceId) {
    this.action = action
    this.resourceId = resourceId
  },

  __dispatchEvent: function(event) {
    // get all listeners. for each listener, ...
    ResourceManager.__listeners.values().each(function(controller) {
      // .. if listener subscribed to this type of event ...
      if (event.action & controller.mask) return controller.listener(event)
    })
  },

  getResource: function(id) {
    // get all possible resources for this.
    id = ERDF.__stripHashes(id)
    var resources = DataManager.query(new ERDF.Resource('#' + id), { prefix: 'raziel', name: 'entry' }, undefined)

    // check for consistency.
    if (resources.length == 1 && resources[0].object.isResource()) {
      var entryUrl = resources[0].object.value
      return new ResourceManager.__Resource(id, entryUrl)
    }

    // else throw an error message.
    throw 'Resource with id ' +
      id +
      ' not recognized as such. ' +
      (resources.length > 1 ? ' There is more than one raziel:entry URL.' : ' There is no raziel:entry URL.')

    return false
  },

  __createResource: function(alternativeDiv) {
    var collectionUrls = DataManager.query(
      new ERDF.Resource(''),
      // TODO This will become raziel:collection in near future.
      { prefix: 'raziel', name: 'collection' },
      undefined
    )

    // check for consistency.

    if (collectionUrls.length == 1 && collectionUrls[0].object.isResource()) {
      // get the collection url.

      var collectionUrl = collectionUrls[0].object.value
      var resource = undefined

      // if there is an old id, serialize the dummy div from there,
      // otherwise create a dummy div on the fly.

      var serialization = alternativeDiv ? alternativeDiv : '<div xmlns="http://www.w3.org/1999/xhtml"></div>'

      ResourceManager.__request(
        'POST',
        collectionUrl,
        serialization,

        // on success
        function() {
          // get div and id that have been generated by the server.

          var response = this.responseXML
          var div = response.childNodes[0]
          var id = div.getAttribute('id')

          // store div in DOM
          if (!$$('.' + PROCESSDATA_REF)[0])
            DataManager.graft(XMLNS.XHTML, document.getElementsByTagNameNS(XMLNS.XHTML, 'body').item(0), [
              'div',
              { class: PROCESSDATA_REF, style: 'display:none;' }
            ])

          $$('.' + PROCESSDATA_REF)[0].appendChild(div.cloneNode(true))

          // parse local erdf data once more.

          DataManager.__synclocal()

          // get new resource object.

          resource = new ResourceManager.getResource(id)

          // set up an action informing of the creation.

          ResourceManager.__resourceActionSucceeded(this, RESOURCE_CREATED, undefined)
        },

        function() {
          ResourceManager.__resourceActionFailed(this, RESOURCE_CREATED, undefined)
        },
        false
      )

      return resource
    }

    // else
    throw 'Could not create resource! raziel:collection URL is missing!'
    return false
  },

  __Resource: function(id, url) {
    this.__id = id
    this.__url = url

    /*
     * Process URL is no longer needed to refer to the shape element on the
     * canvas. AReSS uses the id's to gather information on fireing
     * behaviour now.
     */

    //		// find the process url.
    //		var processUrl = undefined;
    //
    //		var urls = DataManager.query(
    //			new ERDF.Resource('#'+this.__id),
    //			{prefix: 'raziel', name: 'process'},
    //			undefined
    //		);
    //
    //		if(urls.length == 0) { throw 'The resource with the id ' +id+ ' has no process url.'};
    //
    //		urls.each( function(triple) {
    //
    //			// if there are more urls, use the last one.
    //			processUrl = triple.object.value;
    //		});
    //
    //		this.__processUrl = processUrl;
    //
    //		// convenience function for getting the process url.
    //		this.processUrl = function() {
    //			return this.__processUrl;
    //		}

    // convenience finction for getting the id.
    this.id = function() {
      return this.__id
    }

    // convenience finction for getting the entry url.
    this.url = function() {
      return this.__url
    }

    this.reload = function() {
      var _url = this.__url
      var _id = this.__id
      ResourceManager.__request(
        'GET',
        _url,
        null,
        function() {
          ResourceManager.__resourceActionSucceeded(this, RESOURCE_RELOADED, _id)
        },
        function() {
          ResourceManager.__resourceActionFailed(this, RESURCE_RELOADED, _id)
        },
        USE_ASYNCHRONOUS_REQUESTS
      )
    }

    this.save = function(synchronize) {
      var _url = this.__url
      var _id = this.__id
      data = DataManager.serialize($(_id))
      ResourceManager.__request(
        'PUT',
        _url,
        data,
        function() {
          ResourceManager.__resourceActionSucceeded(
            this,
            synchronize ? RESOURCE_SAVED | RESOURCE_SYNCHRONIZED : RESOURCE_SAVED,
            _id
          )
        },
        function() {
          ResourceManager.__resourceActionFailed(
            this,
            synchronize ? RESOURCE_SAVED | RESOURCE_SYNCHRONIZED : RESOURCE.SAVED,
            _id
          )
        },
        USE_ASYNCHRONOUS_REQUESTS
      )
    }

    this.remove = function() {
      var _url = this.__url
      var _id = this.__id
      ResourceManager.__request(
        'DELETE',
        _url,
        null,
        function() {
          ResourceManager.__resourceActionSucceeded(this, RESOURCE_REMOVED, _id)
        },
        function() {
          ResourceManager.__resourceActionFailed(this, RESOURCE_REMOVED, _id)
        },
        USE_ASYNCHRONOUS_REQUESTS
      )
    }
  },

  request: function(url, requestOptions) {
    var options = {
      method: 'get',
      asynchronous: true,
      parameters: {}
    }

    Object.extend(options, requestOptions || {})

    var params = Hash.toQueryString(options.parameters)
    if (params) url += (url.include('?') ? '&' : '?') + params

    return ResourceManager.__request(
      options.method,
      url,
      options.data,
      options.onSuccess instanceof Function
        ? function() {
            options.onSuccess(this)
          }
        : undefined,
      options.onFailure instanceof Function
        ? function() {
            options.onFailure(this)
          }
        : undefined,
      options.asynchronous && USE_ASYNCHRONOUS_REQUESTS,
      options.headers
    )
  },

  __request: function(method, url, data, success, error, async, headers) {
    // get a request object
    var httpRequest = Try.these(
      /* do the Mozilla/Safari/Opera stuff */
      function() {
        return new XMLHttpRequest()
      },

      /* do the IE stuff */
      function() {
        return new ActiveXObject('Msxml2.XMLHTTP')
      },
      function() {
        return new ActiveXObject('Microsoft.XMLHTTP')
      }
    )

    // if there is no request object ...
    if (!httpRequest) {
      if (!this.__corrupt)
        throw 'This browser does not provide any AJAX functionality. You will not be able to use the software provided with the page you are viewing. Please consider installing appropriate extensions.'
      this.__corrupt = true
      return false
    }

    if (success instanceof Function) httpRequest.onload = success
    if (error instanceof Function) {
      httpRequest.onerror = error
    }

    var h = $H(headers)
    h.keys().each(function(key) {
      httpRequest.setRequestHeader(key, h[key])
    })

    try {
      if (SHOW_DEBUG_ALERTS_WHEN_SAVING) alert(method + ' ' + url + '\n' + SHOW_EXTENDED_DEBUG_INFORMATION ? data : '')

      // TODO Remove synchronous calls to the server as soon as xenodot
      // handles asynchronous requests without failure.
      httpRequest.open(method, url, !async ? false : true)
      httpRequest.send(data)
    } catch (e) {
      return false
    }
    return true
  },

  __resourceActionSucceeded: function(transport, action, id) {
    var status = transport.status
    var response = transport.responseText

    if (SHOW_DEBUG_ALERTS_WHEN_SAVING) alert(status + ' ' + url + '\n' + SHOW_EXTENDED_DEBUG_INFORMATION ? data : '')

    // if the status code is not in 2xx, throw an error.
    if (status >= 300)
      throw 'The server responded with an error: ' +
        status +
        '\n' +
        (SHOW_EXTENDED_DEBUG_INFORMATION
          ? +data
          : 'If you need additional information here, including the data sent by the server, consider setting SHOW_EXTENDED_DEBUG_INFORMATION to true.')

    switch (action) {
      case RESOURCE_REMOVED:
        // get div and id
        var response = transport.responseXML
        var div = response.childNodes[0]
        var id = div.getAttribute('id')

        // remove the resource from DOM
        var localDiv = document.getElementById(id)
        localDiv.parentNode.removeChild(localDiv)
        break

      case RESOURCE_CREATED:
        // nothing remains to be done.
        break

      case RESOURCE_SAVED | RESOURCE_SYNCHRONIZED:
        DataManager.__synclocal()

      case RESOURCE_SAVED:
        // nothing remains to be done.
        break

      case RESOURCE_RELOADED:
        // get div and id
        var response = transport.responseXML
        var div = response.childNodes[0]
        var id = div.getAttribute('id')

        // remove the local resource representation from DOM
        var localDiv = document.getElementById(id)
        localDiv.parentNode.removeChild(localDiv)

        // store div in DOM
        if (!$$(PROCESSDATA_REF)[0])
          DataManager.graft(XMLNS.XHTML, document.getElementsByTagNameNS(XMLNS.XHTML, 'body').item(0), [
            'div',
            { class: PROCESSDATA_REF, style: 'display:none;' }
          ])

        $$(PROCESSDATA_REF)[0].appendChild(div.cloneNode(true))
        DataManager.__synclocal()
        break

      default:
        DataManager.__synclocal()
    }

    // dispatch to all listeners ...
    ResourceManager.__dispatchEvent(
      // ... an event describing the change that happened here.
      new ResourceManager.__Event(action, id)
    )
  },

  __resourceActionFailed: function(transport, action, id) {
    throw 'Fatal: Resource action failed. There is something horribly ' +
      'wrong with either the server, the transport protocol or your ' +
      "online status. Sure you're online?"
  }
}
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

/**
 * The super class for all classes in ORYX. Adds some OOP feeling to javascript.
 * See article "Object Oriented Super Class Method Calling with JavaScript" on
 * http://truecode.blogspot.com/2006/08/object-oriented-super-class-method.html
 * for a documentation on this. Fairly good article that points out errors in
 * Douglas Crockford's inheritance and super method calling approach.
 * Worth reading.
 * @class Clazz
 */
var Clazz = function() {}

/**
 * Empty constructor.
 * @methodOf Clazz.prototype
 */
Clazz.prototype.construct = function() {}

/**
 * Can be used to build up inheritances of classes.
 * @example
 * var MyClass = Clazz.extend({
 *   construct: function(myParam){
 *     // Do sth.
 *   }
 * });
 * var MySubClass = MyClass.extend({
 *   construct: function(myParam){
 *     // Use this to call constructor of super class
 *     arguments.callee.$.construct.apply(this, arguments);
 *     // Do sth.
 *   }
 * });
 * @param {Object} def The definition of the new class.
 */
Clazz.extend = function(def) {
  var classDef = function() {
    if (arguments[0] !== Clazz) {
      this.construct.apply(this, arguments)
    }
  }

  var proto = new this(Clazz)
  var superClass = this.prototype

  for (var n in def) {
    var item = def[n]
    if (item instanceof Function) item.$ = superClass
    proto[n] = item
  }

  classDef.prototype = proto

  //Give this new class the same static extend method
  classDef.extend = this.extend
  return classDef
}
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

if (!ORYX) var ORYX = {}

if (!ORYX.CONFIG) ORYX.CONFIG = {}

/**
 * This file contains URI constants that may be used for XMLHTTPRequests.
 */

ORYX.CONFIG.ROOT_PATH = 'editor/' //TODO: Remove last slash!!
ORYX.CONFIG.EXPLORER_PATH = 'explorer'
ORYX.CONFIG.LIBS_PATH = 'libs'

/**
 * Regular Config
 */

ORYX.CONFIG.SERVER_HANDLER_ROOT = 'service'
ORYX.CONFIG.SERVER_EDITOR_HANDLER = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/editor'
ORYX.CONFIG.SERVER_MODEL_HANDLER = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/model'
ORYX.CONFIG.STENCILSET_HANDLER =
  ORYX.CONFIG.SERVER_HANDLER_ROOT + '/editor_stencilset?embedsvg=true&url=true&namespace='
ORYX.CONFIG.STENCIL_SETS_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/editor_stencilset'

ORYX.CONFIG.PLUGINS_CONFIG = 'editor-app/plugins.xml'
ORYX.CONFIG.SYNTAXCHECKER_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/syntaxchecker'
ORYX.CONFIG.DEPLOY_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/model/deploy'
ORYX.CONFIG.MODEL_LIST_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/models'
ORYX.CONFIG.FORM_FLOW_LIST_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/formflows'
ORYX.CONFIG.FORM_FLOW_IMAGE_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/formflow'
ORYX.CONFIG.FORM_LIST_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/forms'
ORYX.CONFIG.FORM_IMAGE_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/form'
ORYX.CONFIG.SUB_PROCESS_LIST_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/subprocesses'
ORYX.CONFIG.SUB_PROCESS_IMAGE_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/subprocess'
ORYX.CONFIG.TEST_SERVICE_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/service/'

ORYX.CONFIG.SERVICE_LIST_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/services'
ORYX.CONFIG.CONDITION_ELEMENT_LIST_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/conditionelements'
ORYX.CONFIG.VARIABLEDEF_ELEMENT_LIST_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/variabledefinitionelements'
ORYX.CONFIG.VALIDATOR_LIST_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/validators'

ORYX.CONFIG.SS_EXTENSIONS_FOLDER = ORYX.CONFIG.ROOT_PATH + 'stencilsets/extensions/'
ORYX.CONFIG.SS_EXTENSIONS_CONFIG = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/editor_ssextensions'
ORYX.CONFIG.ORYX_NEW_URL = '/new'
ORYX.CONFIG.BPMN_LAYOUTER = ORYX.CONFIG.ROOT_PATH + 'bpmnlayouter'

ORYX.CONFIG.EXPRESSION_METADATA_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/expression-metadata'
ORYX.CONFIG.DATASOURCE_METADATA_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/datasource-metadata'
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

if (!ORYX) var ORYX = {}

if (!ORYX.CONFIG) ORYX.CONFIG = {}

/**
 * Signavio specific variables
 */
ORYX.CONFIG.BACKEND_SWITCH = true
ORYX.CONFIG.PANEL_LEFT_WIDTH = 250
ORYX.CONFIG.PANEL_RIGHT_COLLAPSED = true
ORYX.CONFIG.PANEL_RIGHT_WIDTH = 300
ORYX.CONFIG.APPNAME = 'Flowable'
ORYX.CONFIG.WEB_URL = '.'

ORYX.CONFIG.BLANK_IMAGE = ORYX.CONFIG.LIBS_PATH + '/ext-2.0.2/resources/images/default/s.gif'

/* Specify offset of header */
ORYX.CONFIG.OFFSET_HEADER = 61

/* Show grid line while dragging */
ORYX.CONFIG.SHOW_GRIDLINE = true

/* Editor-Mode */
ORYX.CONFIG.MODE_READONLY = 'readonly'
ORYX.CONFIG.MODE_FULLSCREEN = 'fullscreen'
ORYX.CONFIG.WINDOW_HEIGHT = 800
ORYX.CONFIG.PREVENT_LOADINGMASK_AT_READY = false

/* Plugins */
ORYX.CONFIG.PLUGINS_ENABLED = true
ORYX.CONFIG.PLUGINS_FOLDER = 'Plugins/'

ORYX.CONFIG.BPMN20_SCHEMA_VALIDATION_ON = true

/* Namespaces */
ORYX.CONFIG.NAMESPACE_ORYX = 'http://www.b3mn.org/oryx'
ORYX.CONFIG.NAMESPACE_SVG = 'http://www.w3.org/2000/svg'

/* UI */
ORYX.CONFIG.CANVAS_WIDTH = 1200
ORYX.CONFIG.CANVAS_HEIGHT = 1050
ORYX.CONFIG.CANVAS_RESIZE_INTERVAL = 100
ORYX.CONFIG.CANVAS_MIN_WIDTH = 800
ORYX.CONFIG.CANVAS_MIN_HEIGHT = 300
ORYX.CONFIG.SELECTED_AREA_PADDING = 4
ORYX.CONFIG.CANVAS_BACKGROUND_COLOR = 'none'
ORYX.CONFIG.GRID_DISTANCE = 30
ORYX.CONFIG.GRID_ENABLED = true
ORYX.CONFIG.ZOOM_OFFSET = 0.1
ORYX.CONFIG.DEFAULT_SHAPE_MARGIN = 60
ORYX.CONFIG.SCALERS_SIZE = 7
ORYX.CONFIG.MINIMUM_SIZE = 20
ORYX.CONFIG.MAXIMUM_SIZE = 10000
ORYX.CONFIG.OFFSET_MAGNET = 15
ORYX.CONFIG.OFFSET_EDGE_LABEL_TOP = 8
ORYX.CONFIG.OFFSET_EDGE_LABEL_BOTTOM = 8
ORYX.CONFIG.OFFSET_EDGE_BOUNDS = 5
ORYX.CONFIG.COPY_MOVE_OFFSET = 30

ORYX.CONFIG.BORDER_OFFSET = 14

ORYX.CONFIG.MAX_NUM_SHAPES_NO_GROUP = 20 // Updated so the form editor shows all elements at once

ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER = 30
ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET = 45

/* Shape-Menu Align */
ORYX.CONFIG.SHAPEMENU_RIGHT = 'Oryx_Right'
ORYX.CONFIG.SHAPEMENU_BOTTOM = 'Oryx_Bottom'
ORYX.CONFIG.SHAPEMENU_LEFT = 'Oryx_Left'
ORYX.CONFIG.SHAPEMENU_TOP = 'Oryx_Top'

/* Morph-Menu Item */
ORYX.CONFIG.MORPHITEM_DISABLED = 'Oryx_MorphItem_disabled'

/* Property type names */
ORYX.CONFIG.TYPE_STRING = 'string'
ORYX.CONFIG.TYPE_BOOLEAN = 'boolean'
ORYX.CONFIG.TYPE_INTEGER = 'integer'
ORYX.CONFIG.TYPE_FLOAT = 'float'
ORYX.CONFIG.TYPE_COLOR = 'color'
ORYX.CONFIG.TYPE_DATE = 'date'
ORYX.CONFIG.TYPE_CHOICE = 'choice'
ORYX.CONFIG.TYPE_URL = 'url'
ORYX.CONFIG.TYPE_DIAGRAM_LINK = 'diagramlink'
ORYX.CONFIG.TYPE_COMPLEX = 'complex'
ORYX.CONFIG.TYPE_MULTIPLECOMPLEX = 'multiplecomplex'
ORYX.CONFIG.TYPE_TEXT = 'text'
ORYX.CONFIG.TYPE_FLOWABLE_MULTIINSTANCE = 'flowable-multiinstance'
ORYX.CONFIG.TYPE_MODEL_LINK = 'modellink'
ORYX.CONFIG.TYPE_FORM_FLOW_LINK = 'formflowlink'
ORYX.CONFIG.TYPE_FORM_LINK = 'formlink'
ORYX.CONFIG.TYPE_SUB_PROCESS_LINK = 'subprocess-link'
ORYX.CONFIG.TYPE_SERVICE_LINK = 'servicelink'
ORYX.CONFIG.TYPE_CONDITIONS = 'conditions'
ORYX.CONFIG.TYPE_VARIABLES = 'variables'
ORYX.CONFIG.TYPE_LISTENER = 'listener'
ORYX.CONFIG.TYPE_EPC_FREQ = 'epcfrequency'
ORYX.CONFIG.TYPE_GLOSSARY_LINK = 'glossarylink'
ORYX.CONFIG.TYPE_EXPRESSION = 'expression'
ORYX.CONFIG.TYPE_DATASOURCE = 'datasource'
ORYX.CONFIG.TYPE_DATASOURCE_MINIMAL = 'datasource-minimal'
ORYX.CONFIG.TYPE_VALIDATORS = 'validators'
ORYX.CONFIG.TYPE_FLOWABLE_HTTP_REQUEST_METHOD = 'flowable-http-request-method'

/* Vertical line distance of multiline labels */
ORYX.CONFIG.LABEL_LINE_DISTANCE = 2
ORYX.CONFIG.LABEL_DEFAULT_LINE_HEIGHT = 12

/* Open Morph Menu with Hover */
ORYX.CONFIG.ENABLE_MORPHMENU_BY_HOVER = false

/* Editor constants come here */
ORYX.CONFIG.EDITOR_ALIGN_BOTTOM = 0x01
ORYX.CONFIG.EDITOR_ALIGN_MIDDLE = 0x02
ORYX.CONFIG.EDITOR_ALIGN_TOP = 0x04
ORYX.CONFIG.EDITOR_ALIGN_LEFT = 0x08
ORYX.CONFIG.EDITOR_ALIGN_CENTER = 0x10
ORYX.CONFIG.EDITOR_ALIGN_RIGHT = 0x20
ORYX.CONFIG.EDITOR_ALIGN_SIZE = 0x30

/* Event types */
ORYX.CONFIG.EVENT_MOUSEDOWN = 'mousedown'
ORYX.CONFIG.EVENT_MOUSEUP = 'mouseup'
ORYX.CONFIG.EVENT_MOUSEOVER = 'mouseover'
ORYX.CONFIG.EVENT_MOUSEOUT = 'mouseout'
ORYX.CONFIG.EVENT_MOUSEMOVE = 'mousemove'
ORYX.CONFIG.EVENT_DBLCLICK = 'dblclick'
ORYX.CONFIG.EVENT_KEYDOWN = 'keydown'
ORYX.CONFIG.EVENT_KEYUP = 'keyup'

ORYX.CONFIG.EVENT_LOADED = 'editorloaded'
ORYX.CONFIG.EVENT_SAVED = 'editorSaved'

ORYX.CONFIG.EVENT_EXECUTE_COMMANDS = 'executeCommands'
ORYX.CONFIG.EVENT_STENCIL_SET_LOADED = 'stencilSetLoaded'
ORYX.CONFIG.EVENT_SELECTION_CHANGED = 'selectionchanged'
ORYX.CONFIG.EVENT_SHAPEADDED = 'shapeadded'
ORYX.CONFIG.EVENT_SHAPEREMOVED = 'shaperemoved'
ORYX.CONFIG.EVENT_PROPERTY_CHANGED = 'propertyChanged'
ORYX.CONFIG.EVENT_DRAGDROP_START = 'dragdrop.start'
ORYX.CONFIG.EVENT_SHAPE_MENU_CLOSE = 'shape.menu.close'
ORYX.CONFIG.EVENT_DRAGDROP_END = 'dragdrop.end'
ORYX.CONFIG.EVENT_RESIZE_START = 'resize.start'
ORYX.CONFIG.EVENT_RESIZE_END = 'resize.end'
ORYX.CONFIG.EVENT_DRAGDOCKER_DOCKED = 'dragDocker.docked'
ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW = 'highlight.showHighlight'
ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE = 'highlight.hideHighlight'
ORYX.CONFIG.EVENT_LOADING_ENABLE = 'loading.enable'
ORYX.CONFIG.EVENT_LOADING_DISABLE = 'loading.disable'
ORYX.CONFIG.EVENT_LOADING_STATUS = 'loading.status'
ORYX.CONFIG.EVENT_OVERLAY_SHOW = 'overlay.show'
ORYX.CONFIG.EVENT_OVERLAY_HIDE = 'overlay.hide'
ORYX.CONFIG.EVENT_ARRANGEMENT_TOP = 'arrangement.setToTop'
ORYX.CONFIG.EVENT_ARRANGEMENT_BACK = 'arrangement.setToBack'
ORYX.CONFIG.EVENT_ARRANGEMENT_FORWARD = 'arrangement.setForward'
ORYX.CONFIG.EVENT_ARRANGEMENT_BACKWARD = 'arrangement.setBackward'
ORYX.CONFIG.EVENT_PROPWINDOW_PROP_CHANGED = 'propertyWindow.propertyChanged'
ORYX.CONFIG.EVENT_LAYOUT_ROWS = 'layout.rows'
ORYX.CONFIG.EVENT_LAYOUT_BPEL = 'layout.BPEL'
ORYX.CONFIG.EVENT_LAYOUT_BPEL_VERTICAL = 'layout.BPEL.vertical'
ORYX.CONFIG.EVENT_LAYOUT_BPEL_HORIZONTAL = 'layout.BPEL.horizontal'
ORYX.CONFIG.EVENT_LAYOUT_BPEL_SINGLECHILD = 'layout.BPEL.singlechild'
ORYX.CONFIG.EVENT_LAYOUT_BPEL_AUTORESIZE = 'layout.BPEL.autoresize'
ORYX.CONFIG.EVENT_AUTOLAYOUT_LAYOUT = 'autolayout.layout'
ORYX.CONFIG.EVENT_UNDO_EXECUTE = 'undo.execute'
ORYX.CONFIG.EVENT_UNDO_ROLLBACK = 'undo.rollback'
ORYX.CONFIG.EVENT_UNDO_RESET = 'undo.reset'
ORYX.CONFIG.EVENT_BUTTON_UPDATE = 'toolbar.button.update'
ORYX.CONFIG.EVENT_LAYOUT = 'layout.dolayout'
ORYX.CONFIG.EVENT_GLOSSARY_LINK_EDIT = 'glossary.link.edit'
ORYX.CONFIG.EVENT_GLOSSARY_SHOW = 'glossary.show.info'
ORYX.CONFIG.EVENT_GLOSSARY_NEW = 'glossary.show.new'
ORYX.CONFIG.EVENT_DOCKERDRAG = 'dragTheDocker'
ORYX.CONFIG.EVENT_CANVAS_SCROLL = 'canvas.scroll'

ORYX.CONFIG.EVENT_SHOW_PROPERTYWINDOW = 'propertywindow.show'
ORYX.CONFIG.EVENT_ABOUT_TO_SAVE = 'file.aboutToSave'

//extra events
ORYX.CONFIG.EVENT_EDITOR_INIT_COMPLETED = 'editor.init.completed'

//actions events that are fired when a button or key was pressed after completing the initial logic.
ORYX.CONFIG.ACTION_DELETE_COMPLETED = 'delete.action.completed'

/* Selection Shapes Highlights */
ORYX.CONFIG.SELECTION_HIGHLIGHT_SIZE = 5
ORYX.CONFIG.SELECTION_HIGHLIGHT_COLOR = '#4444FF'
ORYX.CONFIG.SELECTION_HIGHLIGHT_COLOR2 = '#9999FF'

ORYX.CONFIG.SELECTION_HIGHLIGHT_STYLE_CORNER = 'corner'
ORYX.CONFIG.SELECTION_HIGHLIGHT_STYLE_RECTANGLE = 'rectangle'

ORYX.CONFIG.SELECTION_VALID_COLOR = '#00FF00'
ORYX.CONFIG.SELECTION_INVALID_COLOR = '#FF0000'

ORYX.CONFIG.DOCKER_DOCKED_COLOR = '#00FF00'
ORYX.CONFIG.DOCKER_UNDOCKED_COLOR = '#FF0000'
ORYX.CONFIG.DOCKER_SNAP_OFFSET = 10

/* Copy & Paste */
ORYX.CONFIG.EDIT_OFFSET_PASTE = 10

/* Key-Codes */
ORYX.CONFIG.KEY_CODE_X = 88
ORYX.CONFIG.KEY_CODE_C = 67
ORYX.CONFIG.KEY_CODE_V = 86
ORYX.CONFIG.KEY_CODE_DELETE = 46
ORYX.CONFIG.KEY_CODE_META = 224
ORYX.CONFIG.KEY_CODE_BACKSPACE = 8
ORYX.CONFIG.KEY_CODE_LEFT = 37
ORYX.CONFIG.KEY_CODE_RIGHT = 39
ORYX.CONFIG.KEY_CODE_UP = 38
ORYX.CONFIG.KEY_CODE_DOWN = 40

// TODO Determine where the lowercase constants are still used and remove them from here.
ORYX.CONFIG.KEY_Code_enter = 12
ORYX.CONFIG.KEY_Code_left = 37
ORYX.CONFIG.KEY_Code_right = 39
ORYX.CONFIG.KEY_Code_top = 38
ORYX.CONFIG.KEY_Code_bottom = 40

/* Supported Meta Keys */

ORYX.CONFIG.META_KEY_META_CTRL = 'metactrl'
ORYX.CONFIG.META_KEY_ALT = 'alt'
ORYX.CONFIG.META_KEY_SHIFT = 'shift'

/* Key Actions */

ORYX.CONFIG.KEY_ACTION_DOWN = 'down'
ORYX.CONFIG.KEY_ACTION_UP = 'up'

/* Form Rowlayouting */
ORYX.CONFIG.FORM_ROW_WIDTH = 350
ORYX.CONFIG.FORM_GROUP_MARGIN = 5
ORYX.CONFIG.FORM_GROUP_EMPTY_HEIGHT = 100

/* Form element types */
ORYX.CONFIG.FORM_ELEMENT_ID_PREFIX = 'http://b3mn.org/stencilset/xforms'
ORYX.CONFIG.FORM_ELEMENT_TYPE_ROOT = 'http://b3mn.org/stencilset/xforms#XForm'
ORYX.CONFIG.FORM_ELEMENT_TYPE_GROUP = 'http://b3mn.org/stencilset/xforms#Group'
ORYX.CONFIG.FORM_ELEMENT_TYPE_REPEATING_GROUP = 'http://b3mn.org/stencilset/xforms#RepeatingGroup'
ORYX.CONFIG.FORM_ELEMENT_TYPE_LABEL_FIELD = 'http://b3mn.org/stencilset/xforms#LabelField'

/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

function printf() {
  var result = arguments[0]
  for (var i = 1; i < arguments.length; i++) result = result.replace('%' + (i - 1), arguments[i])
  return result
}

// oryx constants.
var ORYX_LOGLEVEL_TRACE = 5
var ORYX_LOGLEVEL_DEBUG = 4
var ORYX_LOGLEVEL_INFO = 3
var ORYX_LOGLEVEL_WARN = 2
var ORYX_LOGLEVEL_ERROR = 1
var ORYX_LOGLEVEL_FATAL = 0
var ORYX_LOGLEVEL = 3
var ORYX_CONFIGURATION_DELAY = 100
var ORYX_CONFIGURATION_WAIT_ATTEMPTS = 10

if (!ORYX) var ORYX = {}

ORYX = Object.extend(ORYX, {
  //set the path in the config.js file!!!!
  PATH: ORYX.CONFIG.ROOT_PATH,
  //CONFIGURATION: "config.js",

  URLS: [],

  alreadyLoaded: [],

  configrationRetries: 0,

  Version: '0.1.1',

  availablePlugins: [],

  /**
   * The ORYX.Log logger.
   */
  Log: {
    __appenders: [
      {
        append: function(message) {
          if (typeof console !== 'undefined' && console.log !== undefined) {
            // console.log(message);
          }
        }
      }
    ],

    trace: function() {
      if (ORYX_LOGLEVEL >= ORYX_LOGLEVEL_TRACE) ORYX.Log.__log('TRACE', arguments)
    },
    debug: function() {
      if (ORYX_LOGLEVEL >= ORYX_LOGLEVEL_DEBUG) ORYX.Log.__log('DEBUG', arguments)
    },
    info: function() {
      if (ORYX_LOGLEVEL >= ORYX_LOGLEVEL_INFO) ORYX.Log.__log('INFO', arguments)
    },
    warn: function() {
      if (ORYX_LOGLEVEL >= ORYX_LOGLEVEL_WARN) ORYX.Log.__log('WARN', arguments)
    },
    error: function() {
      if (ORYX_LOGLEVEL >= ORYX_LOGLEVEL_ERROR) ORYX.Log.__log('ERROR', arguments)
    },
    fatal: function() {
      if (ORYX_LOGLEVEL >= ORYX_LOGLEVEL_FATAL) ORYX.Log.__log('FATAL', arguments)
    },

    __log: function(prefix, messageParts) {
      messageParts[0] = new Date().getTime() + ' ' + prefix + ' ' + messageParts[0]
      var message = printf.apply(null, messageParts)

      ORYX.Log.__appenders.each(function(appender) {
        appender.append(message)
      })
    },

    addAppender: function(appender) {
      ORYX.Log.__appenders.push(appender)
    }
  },

  /**
   * First bootstrapping layer. The Oryx loading procedure begins. In this
   * step, all preliminaries that are not in the responsibility of Oryx to be
   * met have to be checked here, such as the existance of the prototpe
   * library in the current execution environment. After that, the second
   * bootstrapping layer is being invoked. Failing to ensure that any
   * preliminary condition is not met has to fail with an error.
   */
  load: function() {
    ORYX.Log.debug('Oryx begins loading procedure.')

    // check for prototype
    if (
      typeof Prototype == 'undefined' ||
      typeof Element == 'undefined' ||
      typeof Element.Methods == 'undefined' ||
      parseFloat(Prototype.Version.split('.')[0] + '.' + Prototype.Version.split('.')[1]) < 1.5
    )
      throw 'Application requires the Prototype JavaScript framework >= 1.5.3'

    ORYX.Log.debug('Prototype > 1.5 found.')

    // continue loading.
    ORYX._load()
  },

  /**
   * Second bootstrapping layer. The oryx configuration is checked. When not
   * yet loaded, config.js is being requested from the server. A repeated
   * error in retrieving the configuration will result in an error to be
   * thrown after a certain time of retries. Once the configuration is there,
   * all urls that are registered with oryx loading are being requested from
   * the server. Once everything is loaded, the third layer is being invoked.
   */
  _load: function() {
    ORYX.loadPlugins()
  },

  /**
   * Third bootstrapping layer. This is where first the plugin coniguration
   * file is loaded into oryx, analyzed, and where all plugins are being
   * requested by the server. Afterwards, all editor instances will be
   * initialized.
   */
  loadPlugins: function() {
    // load plugins if enabled.
    if (ORYX.CONFIG.PLUGINS_ENABLED) ORYX._loadPlugins()
    else ORYX.Log.warn('Ignoring plugins, loading Core only.')

    // init the editor instances.
    init()
  },

  _loadPlugins: function(plugins) {
    ORYX.availablePlugins.length = 0

    // get plugins.xml content
    var resultXml = jQuery.parseXML(plugins) //jquery parser

    // TODO: Describe how properties are handled.
    // Get the globale Properties
    var globalProperties = []
    var preferences = $A(resultXml.getElementsByTagName('properties'))
    preferences.each(function(p) {
      var props = $A(p.childNodes)
      props.each(function(prop) {
        var property = new Hash()

        // get all attributes from the node and set to global properties
        var attributes = $A(prop.attributes)
        attributes.each(function(attr) {
          property.set(attr.nodeName, attr.nodeValue)
        })
        if (attributes.length > 0) {
          globalProperties.push(property)
        }
      })
    })

    // TODO Why are we using XML if we don't respect structure anyway?
    // for each plugin element in the configuration..
    var plugin = resultXml.getElementsByTagName('plugin')
    $A(plugin).each(function(node) {
      // get all element's attributes.
      // TODO: What about: var pluginData = $H(node.attributes) !?
      var pluginData = new Hash()
      $A(node.attributes).each(function(attr) {
        pluginData.set(attr.nodeName, attr.nodeValue)
      })

      // ensure there's a name attribute.
      if (!pluginData.get('name')) {
        ORYX.Log.error('A plugin is not providing a name. Ingnoring this plugin.')
        return
      }

      // ensure there's a source attribute.
      if (!pluginData.get('source')) {
        ORYX.Log.error("Plugin with name '%0' doesn't provide a source attribute.", pluginData.get('name'))
        return
      }

      // Get all private Properties
      var propertyNodes = node.getElementsByTagName('property')
      var properties = []
      $A(propertyNodes).each(function(prop) {
        var property = new Hash()

        // Get all Attributes from the Node
        var attributes = $A(prop.attributes)
        attributes.each(function(attr) {
          property.set(attr.nodeName, attr.nodeValue)
        })

        if (attributes.length > 0) {
          properties.push(property)
        }
      })

      // Set all Global-Properties to the Properties
      properties = properties.concat(globalProperties)

      // Set Properties to Plugin-Data
      pluginData.set('properties', properties)

      // Get the RequieredNodes
      var requireNodes = node.getElementsByTagName('requires')
      var requires
      $A(requireNodes).each(function(req) {
        var namespace = $A(req.attributes).find(function(attr) {
          return attr.name == 'namespace'
        })
        if (namespace && namespace.nodeValue) {
          if (!requires) {
            requires = { namespaces: [] }
          }

          requires.namespaces.push(namespace.nodeValue)
        }
      })

      // Set Requires to the Plugin-Data, if there is one
      if (requires) {
        pluginData.set('requires', requires)
      }

      // Get the RequieredNodes
      var notUsesInNodes = node.getElementsByTagName('notUsesIn')
      var notUsesIn
      $A(notUsesInNodes).each(function(not) {
        var namespace = $A(not.attributes).find(function(attr) {
          return attr.name == 'namespace'
        })
        if (namespace && namespace.nodeValue) {
          if (!notUsesIn) {
            notUsesIn = { namespaces: [] }
          }

          notUsesIn.namespaces.push(namespace.nodeValue)
        }
      })

      // Set Requires to the Plugin-Data, if there is one
      if (notUsesIn) {
        pluginData.set('notUsesIn', notUsesIn)
      }

      var url = ORYX.PATH + ORYX.CONFIG.PLUGINS_FOLDER + pluginData.get('source')

      ORYX.Log.debug("Requireing '%0'", url)

      // Add the Script-Tag to the Site
      //Kickstart.require(url);

      ORYX.Log.info("Plugin '%0' successfully loaded.", pluginData.get('name'))

      // Add the Plugin-Data to all available Plugins
      ORYX.availablePlugins.push(pluginData)
    })
  },

  _loadPluginsOnFails: function(result) {
    ORYX.Log.error('Plugin configuration file not available.')
  }
})

/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

/**
 * Init namespaces
 */
if (!ORYX) {
  var ORYX = {}
}
if (!ORYX.Core) {
  ORYX.Core = {}
}
if (!ORYX.Core.SVG) {
  ORYX.Core.SVG = {}
}

/**
 * EditPathHandler
 *
 * Edit SVG paths' coordinates according to specified from-to movement and
 * horizontal and vertical scaling factors.
 * The resulting path's d attribute is stored in instance variable d.
 *
 * @constructor
 */
ORYX.Core.SVG.EditPathHandler = Clazz.extend({
  construct: function() {
    arguments.callee.$.construct.apply(this, arguments)

    this.x = 0
    this.y = 0
    this.oldX = 0
    this.oldY = 0
    this.deltaWidth = 1
    this.deltaHeight = 1

    this.d = ''
  },

  /**
   * init
   *
   * @param {float} x Target point's x-coordinate
   * @param {float} y Target point's y-coordinate
   * @param {float} oldX Reference point's x-coordinate
   * @param {float} oldY Reference point's y-coordinate
   * @param {float} deltaWidth Horizontal scaling factor
   * @param {float} deltaHeight Vertical scaling factor
   */
  init: function(x, y, oldX, oldY, deltaWidth, deltaHeight) {
    this.x = x
    this.y = y
    this.oldX = oldX
    this.oldY = oldY
    this.deltaWidth = deltaWidth
    this.deltaHeight = deltaHeight

    this.d = ''
  },

  /**
   * editPointsAbs
   *
   * @param {Array} points Array of absolutePoints
   */
  editPointsAbs: function(points) {
    if (points instanceof Array) {
      var newPoints = []
      var x, y
      for (var i = 0; i < points.length; i++) {
        x = (parseFloat(points[i]) - this.oldX) * this.deltaWidth + this.x
        i++
        y = (parseFloat(points[i]) - this.oldY) * this.deltaHeight + this.y
        newPoints.push(x)
        newPoints.push(y)
      }

      return newPoints
    } else {
      //TODO error
    }
  },

  /**
   * editPointsRel
   *
   * @param {Array} points Array of absolutePoints
   */
  editPointsRel: function(points) {
    if (points instanceof Array) {
      var newPoints = []
      var x, y
      for (var i = 0; i < points.length; i++) {
        x = parseFloat(points[i]) * this.deltaWidth
        i++
        y = parseFloat(points[i]) * this.deltaHeight
        newPoints.push(x)
        newPoints.push(y)
      }

      return newPoints
    } else {
      //TODO error
    }
  },

  /**
   * arcAbs - A
   *
   * @param {Number} rx
   * @param {Number} ry
   * @param {Number} xAxisRotation
   * @param {Boolean} largeArcFlag
   * @param {Boolean} sweepFlag
   * @param {Number} x
   * @param {Number} y
   */
  arcAbs: function(rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y) {
    var pointsAbs = this.editPointsAbs([x, y])
    var pointsRel = this.editPointsRel([rx, ry])

    this.d = this.d.concat(
      ' A' +
        pointsRel[0] +
        ' ' +
        pointsRel[1] +
        ' ' +
        xAxisRotation +
        ' ' +
        largeArcFlag +
        ' ' +
        sweepFlag +
        ' ' +
        pointsAbs[0] +
        ' ' +
        pointsAbs[1] +
        ' '
    )
  },

  /**
   * arcRel - a
   *
   * @param {Number} rx
   * @param {Number} ry
   * @param {Number} xAxisRotation
   * @param {Boolean} largeArcFlag
   * @param {Boolean} sweepFlag
   * @param {Number} x
   * @param {Number} y
   */
  arcRel: function(rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y) {
    var pointsRel = this.editPointsRel([rx, ry, x, y])

    this.d = this.d.concat(
      ' a' +
        pointsRel[0] +
        ' ' +
        pointsRel[1] +
        ' ' +
        xAxisRotation +
        ' ' +
        largeArcFlag +
        ' ' +
        sweepFlag +
        ' ' +
        pointsRel[2] +
        ' ' +
        pointsRel[3] +
        ' '
    )
  },

  /**
   * curvetoCubicAbs - C
   *
   * @param {Number} x1
   * @param {Number} y1
   * @param {Number} x2
   * @param {Number} y2
   * @param {Number} x
   * @param {Number} y
   */
  curvetoCubicAbs: function(x1, y1, x2, y2, x, y) {
    var pointsAbs = this.editPointsAbs([x1, y1, x2, y2, x, y])

    this.d = this.d.concat(
      ' C' +
        pointsAbs[0] +
        ' ' +
        pointsAbs[1] +
        ' ' +
        pointsAbs[2] +
        ' ' +
        pointsAbs[3] +
        ' ' +
        pointsAbs[4] +
        ' ' +
        pointsAbs[5] +
        ' '
    )
  },

  /**
   * curvetoCubicRel - c
   *
   * @param {Number} x1
   * @param {Number} y1
   * @param {Number} x2
   * @param {Number} y2
   * @param {Number} x
   * @param {Number} y
   */
  curvetoCubicRel: function(x1, y1, x2, y2, x, y) {
    var pointsRel = this.editPointsRel([x1, y1, x2, y2, x, y])

    this.d = this.d.concat(
      ' c' +
        pointsRel[0] +
        ' ' +
        pointsRel[1] +
        ' ' +
        pointsRel[2] +
        ' ' +
        pointsRel[3] +
        ' ' +
        pointsRel[4] +
        ' ' +
        pointsRel[5] +
        ' '
    )
  },

  /**
   * linetoHorizontalAbs - H
   *
   * @param {Number} x
   */
  linetoHorizontalAbs: function(x) {
    var pointsAbs = this.editPointsAbs([x, 0])

    this.d = this.d.concat(' H' + pointsAbs[0] + ' ')
  },

  /**
   * linetoHorizontalRel - h
   *
   * @param {Number} x
   */
  linetoHorizontalRel: function(x) {
    var pointsRel = this.editPointsRel([x, 0])

    this.d = this.d.concat(' h' + pointsRel[0] + ' ')
  },

  /**
   * linetoAbs - L
   *
   * @param {Number} x
   * @param {Number} y
   */
  linetoAbs: function(x, y) {
    var pointsAbs = this.editPointsAbs([x, y])

    this.d = this.d.concat(' L' + pointsAbs[0] + ' ' + pointsAbs[1] + ' ')
  },

  /**
   * linetoRel - l
   *
   * @param {Number} x
   * @param {Number} y
   */
  linetoRel: function(x, y) {
    var pointsRel = this.editPointsRel([x, y])

    this.d = this.d.concat(' l' + pointsRel[0] + ' ' + pointsRel[1] + ' ')
  },

  /**
   * movetoAbs - M
   *
   * @param {Number} x
   * @param {Number} y
   */
  movetoAbs: function(x, y) {
    var pointsAbs = this.editPointsAbs([x, y])

    this.d = this.d.concat(' M' + pointsAbs[0] + ' ' + pointsAbs[1] + ' ')
  },

  /**
   * movetoRel - m
   *
   * @param {Number} x
   * @param {Number} y
   */
  movetoRel: function(x, y) {
    var pointsRel
    if (this.d === '') {
      pointsRel = this.editPointsAbs([x, y])
    } else {
      pointsRel = this.editPointsRel([x, y])
    }

    this.d = this.d.concat(' m' + pointsRel[0] + ' ' + pointsRel[1] + ' ')
  },

  /**
   * curvetoQuadraticAbs - Q
   *
   * @param {Number} x1
   * @param {Number} y1
   * @param {Number} x
   * @param {Number} y
   */
  curvetoQuadraticAbs: function(x1, y1, x, y) {
    var pointsAbs = this.editPointsAbs([x1, y1, x, y])

    this.d = this.d.concat(' Q' + pointsAbs[0] + ' ' + pointsAbs[1] + ' ' + pointsAbs[2] + ' ' + pointsAbs[3] + ' ')
  },

  /**
   * curvetoQuadraticRel - q
   *
   * @param {Number} x1
   * @param {Number} y1
   * @param {Number} x
   * @param {Number} y
   */
  curvetoQuadraticRel: function(x1, y1, x, y) {
    var pointsRel = this.editPointsRel([x1, y1, x, y])

    this.d = this.d.concat(' q' + pointsRel[0] + ' ' + pointsRel[1] + ' ' + pointsRel[2] + ' ' + pointsRel[3] + ' ')
  },

  /**
   * curvetoCubicSmoothAbs - S
   *
   * @param {Number} x2
   * @param {Number} y2
   * @param {Number} x
   * @param {Number} y
   */
  curvetoCubicSmoothAbs: function(x2, y2, x, y) {
    var pointsAbs = this.editPointsAbs([x2, y2, x, y])

    this.d = this.d.concat(' S' + pointsAbs[0] + ' ' + pointsAbs[1] + ' ' + pointsAbs[2] + ' ' + pointsAbs[3] + ' ')
  },

  /**
   * curvetoCubicSmoothRel - s
   *
   * @param {Number} x2
   * @param {Number} y2
   * @param {Number} x
   * @param {Number} y
   */
  curvetoCubicSmoothRel: function(x2, y2, x, y) {
    var pointsRel = this.editPointsRel([x2, y2, x, y])

    this.d = this.d.concat(' s' + pointsRel[0] + ' ' + pointsRel[1] + ' ' + pointsRel[2] + ' ' + pointsRel[3] + ' ')
  },

  /**
   * curvetoQuadraticSmoothAbs - T
   *
   * @param {Number} x
   * @param {Number} y
   */
  curvetoQuadraticSmoothAbs: function(x, y) {
    var pointsAbs = this.editPointsAbs([x, y])

    this.d = this.d.concat(' T' + pointsAbs[0] + ' ' + pointsAbs[1] + ' ')
  },

  /**
   * curvetoQuadraticSmoothRel - t
   *
   * @param {Number} x
   * @param {Number} y
   */
  curvetoQuadraticSmoothRel: function(x, y) {
    var pointsRel = this.editPointsRel([x, y])

    this.d = this.d.concat(' t' + pointsRel[0] + ' ' + pointsRel[1] + ' ')
  },

  /**
   * linetoVerticalAbs - V
   *
   * @param {Number} y
   */
  linetoVerticalAbs: function(y) {
    var pointsAbs = this.editPointsAbs([0, y])

    this.d = this.d.concat(' V' + pointsAbs[1] + ' ')
  },

  /**
   * linetoVerticalRel - v
   *
   * @param {Number} y
   */
  linetoVerticalRel: function(y) {
    var pointsRel = this.editPointsRel([0, y])

    this.d = this.d.concat(' v' + pointsRel[1] + ' ')
  },

  /**
   * closePath - z or Z
   */
  closePath: function() {
    this.d = this.d.concat(' z')
  }
})
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

/**
 * Init namespaces
 */
if (!ORYX) {
  var ORYX = {}
}
if (!ORYX.Core) {
  ORYX.Core = {}
}
if (!ORYX.Core.SVG) {
  ORYX.Core.SVG = {}
}

/**
 * MinMaxPathHandler
 *
 * Determine the minimum and maximum of a SVG path's absolute coordinates.
 * For relative coordinates the absolute value is computed for consideration.
 * The values are stored in attributes minX, minY, maxX, and maxY.
 *
 * @constructor
 */
ORYX.Core.SVG.MinMaxPathHandler = Clazz.extend({
  construct: function() {
    arguments.callee.$.construct.apply(this, arguments)

    this.minX = undefined
    this.minY = undefined
    this.maxX = undefined
    this.maxY = undefined

    this._lastAbsX = undefined
    this._lastAbsY = undefined
  },

  /**
   * Store minimal and maximal coordinates of passed points to attributes minX, maxX, minY, maxY
   *
   * @param {Array} points Array of absolutePoints
   */
  calculateMinMax: function(points) {
    if (points instanceof Array) {
      var x, y
      for (var i = 0; i < points.length; i++) {
        x = parseFloat(points[i])
        i++
        y = parseFloat(points[i])

        this.minX = this.minX !== undefined ? Math.min(this.minX, x) : x
        this.maxX = this.maxX !== undefined ? Math.max(this.maxX, x) : x
        this.minY = this.minY !== undefined ? Math.min(this.minY, y) : y
        this.maxY = this.maxY !== undefined ? Math.max(this.maxY, y) : y

        this._lastAbsX = x
        this._lastAbsY = y
      }
    } else {
      //TODO error
    }
  },

  /**
   * arcAbs - A
   *
   * @param {Number} rx
   * @param {Number} ry
   * @param {Number} xAxisRotation
   * @param {Boolean} largeArcFlag
   * @param {Boolean} sweepFlag
   * @param {Number} x
   * @param {Number} y
   */
  arcAbs: function(rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y) {
    this.calculateMinMax([x, y])
  },

  /**
   * arcRel - a
   *
   * @param {Number} rx
   * @param {Number} ry
   * @param {Number} xAxisRotation
   * @param {Boolean} largeArcFlag
   * @param {Boolean} sweepFlag
   * @param {Number} x
   * @param {Number} y
   */
  arcRel: function(rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y) {
    this.calculateMinMax([this._lastAbsX + x, this._lastAbsY + y])
  },

  /**
   * curvetoCubicAbs - C
   *
   * @param {Number} x1
   * @param {Number} y1
   * @param {Number} x2
   * @param {Number} y2
   * @param {Number} x
   * @param {Number} y
   */
  curvetoCubicAbs: function(x1, y1, x2, y2, x, y) {
    this.calculateMinMax([x1, y1, x2, y2, x, y])
  },

  /**
   * curvetoCubicRel - c
   *
   * @param {Number} x1
   * @param {Number} y1
   * @param {Number} x2
   * @param {Number} y2
   * @param {Number} x
   * @param {Number} y
   */
  curvetoCubicRel: function(x1, y1, x2, y2, x, y) {
    this.calculateMinMax([
      this._lastAbsX + x1,
      this._lastAbsY + y1,
      this._lastAbsX + x2,
      this._lastAbsY + y2,
      this._lastAbsX + x,
      this._lastAbsY + y
    ])
  },

  /**
   * linetoHorizontalAbs - H
   *
   * @param {Number} x
   */
  linetoHorizontalAbs: function(x) {
    this.calculateMinMax([x, this._lastAbsY])
  },

  /**
   * linetoHorizontalRel - h
   *
   * @param {Number} x
   */
  linetoHorizontalRel: function(x) {
    this.calculateMinMax([this._lastAbsX + x, this._lastAbsY])
  },

  /**
   * linetoAbs - L
   *
   * @param {Number} x
   * @param {Number} y
   */
  linetoAbs: function(x, y) {
    this.calculateMinMax([x, y])
  },

  /**
   * linetoRel - l
   *
   * @param {Number} x
   * @param {Number} y
   */
  linetoRel: function(x, y) {
    this.calculateMinMax([this._lastAbsX + x, this._lastAbsY + y])
  },

  /**
   * movetoAbs - M
   *
   * @param {Number} x
   * @param {Number} y
   */
  movetoAbs: function(x, y) {
    this.calculateMinMax([x, y])
  },

  /**
   * movetoRel - m
   *
   * @param {Number} x
   * @param {Number} y
   */
  movetoRel: function(x, y) {
    if (this._lastAbsX && this._lastAbsY) {
      this.calculateMinMax([this._lastAbsX + x, this._lastAbsY + y])
    } else {
      this.calculateMinMax([x, y])
    }
  },

  /**
   * curvetoQuadraticAbs - Q
   *
   * @param {Number} x1
   * @param {Number} y1
   * @param {Number} x
   * @param {Number} y
   */
  curvetoQuadraticAbs: function(x1, y1, x, y) {
    this.calculateMinMax([x1, y1, x, y])
  },

  /**
   * curvetoQuadraticRel - q
   *
   * @param {Number} x1
   * @param {Number} y1
   * @param {Number} x
   * @param {Number} y
   */
  curvetoQuadraticRel: function(x1, y1, x, y) {
    this.calculateMinMax([this._lastAbsX + x1, this._lastAbsY + y1, this._lastAbsX + x, this._lastAbsY + y])
  },

  /**
   * curvetoCubicSmoothAbs - S
   *
   * @param {Number} x2
   * @param {Number} y2
   * @param {Number} x
   * @param {Number} y
   */
  curvetoCubicSmoothAbs: function(x2, y2, x, y) {
    this.calculateMinMax([x2, y2, x, y])
  },

  /**
   * curvetoCubicSmoothRel - s
   *
   * @param {Number} x2
   * @param {Number} y2
   * @param {Number} x
   * @param {Number} y
   */
  curvetoCubicSmoothRel: function(x2, y2, x, y) {
    this.calculateMinMax([this._lastAbsX + x2, this._lastAbsY + y2, this._lastAbsX + x, this._lastAbsY + y])
  },

  /**
   * curvetoQuadraticSmoothAbs - T
   *
   * @param {Number} x
   * @param {Number} y
   */
  curvetoQuadraticSmoothAbs: function(x, y) {
    this.calculateMinMax([x, y])
  },

  /**
   * curvetoQuadraticSmoothRel - t
   *
   * @param {Number} x
   * @param {Number} y
   */
  curvetoQuadraticSmoothRel: function(x, y) {
    this.calculateMinMax([this._lastAbsX + x, this._lastAbsY + y])
  },

  /**
   * linetoVerticalAbs - V
   *
   * @param {Number} y
   */
  linetoVerticalAbs: function(y) {
    this.calculateMinMax([this._lastAbsX, y])
  },

  /**
   * linetoVerticalRel - v
   *
   * @param {Number} y
   */
  linetoVerticalRel: function(y) {
    this.calculateMinMax([this._lastAbsX, this._lastAbsY + y])
  },

  /**
   * closePath - z or Z
   */
  closePath: function() {
    return // do nothing
  }
})
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

/**
 * Init namespaces
 */
if (!ORYX) {
  var ORYX = {}
}
if (!ORYX.Core) {
  ORYX.Core = {}
}
if (!ORYX.Core.SVG) {
  ORYX.Core.SVG = {}
}

/**
 * PathHandler
 *
 * Determine absolute points of a SVG path. The coordinates are stored
 * sequentially in the attribute points (x-coordinates at even indices,
 * y-coordinates at odd indices).
 *
 * @constructor
 */
ORYX.Core.SVG.PointsPathHandler = Clazz.extend({
  construct: function() {
    arguments.callee.$.construct.apply(this, arguments)

    this.points = []

    this._lastAbsX = undefined
    this._lastAbsY = undefined
  },

  /**
   * addPoints
   *
   * @param {Array} points Array of absolutePoints
   */
  addPoints: function(points) {
    if (points instanceof Array) {
      var x, y
      for (var i = 0; i < points.length; i++) {
        x = parseFloat(points[i])
        i++
        y = parseFloat(points[i])

        this.points.push(x)
        this.points.push(y)
        //this.points.push({x:x, y:y});

        this._lastAbsX = x
        this._lastAbsY = y
      }
    } else {
      //TODO error
    }
  },

  /**
   * arcAbs - A
   *
   * @param {Number} rx
   * @param {Number} ry
   * @param {Number} xAxisRotation
   * @param {Boolean} largeArcFlag
   * @param {Boolean} sweepFlag
   * @param {Number} x
   * @param {Number} y
   */
  arcAbs: function(rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y) {
    this.addPoints([x, y])
  },

  /**
   * arcRel - a
   *
   * @param {Number} rx
   * @param {Number} ry
   * @param {Number} xAxisRotation
   * @param {Boolean} largeArcFlag
   * @param {Boolean} sweepFlag
   * @param {Number} x
   * @param {Number} y
   */
  arcRel: function(rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y) {
    this.addPoints([this._lastAbsX + x, this._lastAbsY + y])
  },

  /**
   * curvetoCubicAbs - C
   *
   * @param {Number} x1
   * @param {Number} y1
   * @param {Number} x2
   * @param {Number} y2
   * @param {Number} x
   * @param {Number} y
   */
  curvetoCubicAbs: function(x1, y1, x2, y2, x, y) {
    this.addPoints([x, y])
  },

  /**
   * curvetoCubicRel - c
   *
   * @param {Number} x1
   * @param {Number} y1
   * @param {Number} x2
   * @param {Number} y2
   * @param {Number} x
   * @param {Number} y
   */
  curvetoCubicRel: function(x1, y1, x2, y2, x, y) {
    this.addPoints([this._lastAbsX + x, this._lastAbsY + y])
  },

  /**
   * linetoHorizontalAbs - H
   *
   * @param {Number} x
   */
  linetoHorizontalAbs: function(x) {
    this.addPoints([x, this._lastAbsY])
  },

  /**
   * linetoHorizontalRel - h
   *
   * @param {Number} x
   */
  linetoHorizontalRel: function(x) {
    this.addPoints([this._lastAbsX + x, this._lastAbsY])
  },

  /**
   * linetoAbs - L
   *
   * @param {Number} x
   * @param {Number} y
   */
  linetoAbs: function(x, y) {
    this.addPoints([x, y])
  },

  /**
   * linetoRel - l
   *
   * @param {Number} x
   * @param {Number} y
   */
  linetoRel: function(x, y) {
    this.addPoints([this._lastAbsX + x, this._lastAbsY + y])
  },

  /**
   * movetoAbs - M
   *
   * @param {Number} x
   * @param {Number} y
   */
  movetoAbs: function(x, y) {
    this.addPoints([x, y])
  },

  /**
   * movetoRel - m
   *
   * @param {Number} x
   * @param {Number} y
   */
  movetoRel: function(x, y) {
    if (this._lastAbsX && this._lastAbsY) {
      this.addPoints([this._lastAbsX + x, this._lastAbsY + y])
    } else {
      this.addPoints([x, y])
    }
  },

  /**
   * curvetoQuadraticAbs - Q
   *
   * @param {Number} x1
   * @param {Number} y1
   * @param {Number} x
   * @param {Number} y
   */
  curvetoQuadraticAbs: function(x1, y1, x, y) {
    this.addPoints([x, y])
  },

  /**
   * curvetoQuadraticRel - q
   *
   * @param {Number} x1
   * @param {Number} y1
   * @param {Number} x
   * @param {Number} y
   */
  curvetoQuadraticRel: function(x1, y1, x, y) {
    this.addPoints([this._lastAbsX + x, this._lastAbsY + y])
  },

  /**
   * curvetoCubicSmoothAbs - S
   *
   * @param {Number} x2
   * @param {Number} y2
   * @param {Number} x
   * @param {Number} y
   */
  curvetoCubicSmoothAbs: function(x2, y2, x, y) {
    this.addPoints([x, y])
  },

  /**
   * curvetoCubicSmoothRel - s
   *
   * @param {Number} x2
   * @param {Number} y2
   * @param {Number} x
   * @param {Number} y
   */
  curvetoCubicSmoothRel: function(x2, y2, x, y) {
    this.addPoints([this._lastAbsX + x, this._lastAbsY + y])
  },

  /**
   * curvetoQuadraticSmoothAbs - T
   *
   * @param {Number} x
   * @param {Number} y
   */
  curvetoQuadraticSmoothAbs: function(x, y) {
    this.addPoints([x, y])
  },

  /**
   * curvetoQuadraticSmoothRel - t
   *
   * @param {Number} x
   * @param {Number} y
   */
  curvetoQuadraticSmoothRel: function(x, y) {
    this.addPoints([this._lastAbsX + x, this._lastAbsY + y])
  },

  /**
   * linetoVerticalAbs - V
   *
   * @param {Number} y
   */
  linetoVerticalAbs: function(y) {
    this.addPoints([this._lastAbsX, y])
  },

  /**
   * linetoVerticalRel - v
   *
   * @param {Number} y
   */
  linetoVerticalRel: function(y) {
    this.addPoints([this._lastAbsX, this._lastAbsY + y])
  },

  /**
   * closePath - z or Z
   */
  closePath: function() {
    return // do nothing
  }
})
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

/**
 *
 * Config variables
 */
NAMESPACE_ORYX = 'http://www.b3mn.org/oryx'
NAMESPACE_SVG = 'http://www.w3.org/2000/svg/'

/**
 * @classDescription This class wraps the manipulation of a SVG marker.
 * @namespace ORYX.Core.SVG
 * uses Inheritance (Clazz)
 * uses Prototype 1.5.0
 *
 */

/**
 * Init package
 */
if (!ORYX) {
  var ORYX = {}
}
if (!ORYX.Core) {
  ORYX.Core = {}
}
if (!ORYX.Core.SVG) {
  ORYX.Core.SVG = {}
}

ORYX.Core.SVG.SVGMarker = Clazz.extend({
  /**
   * Constructor
   * @param markerElement {SVGMarkerElement}
   */
  construct: function(markerElement) {
    arguments.callee.$.construct.apply(this, arguments)

    this.id = undefined
    this.element = markerElement
    this.refX = undefined
    this.refY = undefined
    this.markerWidth = undefined
    this.markerHeight = undefined
    this.oldRefX = undefined
    this.oldRefY = undefined
    this.oldMarkerWidth = undefined
    this.oldMarkerHeight = undefined
    this.optional = false
    this.enabled = true
    this.minimumLength = undefined
    this.resize = false

    this.svgShapes = []

    this._init() //initialisation of all the properties declared above.
  },

  /**
   * Initializes the values that are defined in the constructor.
   */
  _init: function() {
    //check if this.element is a SVGMarkerElement
    if (!(this.element == '[object SVGMarkerElement]')) {
      throw 'SVGMarker: Argument is not an instance of SVGMarkerElement.'
    }

    this.id = this.element.getAttributeNS(null, 'id')

    //init svg marker attributes
    var refXValue = this.element.getAttributeNS(null, 'refX')
    if (refXValue) {
      this.refX = parseFloat(refXValue)
    } else {
      this.refX = 0
    }
    var refYValue = this.element.getAttributeNS(null, 'refY')
    if (refYValue) {
      this.refY = parseFloat(refYValue)
    } else {
      this.refY = 0
    }
    var markerWidthValue = this.element.getAttributeNS(null, 'markerWidth')
    if (markerWidthValue) {
      this.markerWidth = parseFloat(markerWidthValue)
    } else {
      this.markerWidth = 3
    }
    var markerHeightValue = this.element.getAttributeNS(null, 'markerHeight')
    if (markerHeightValue) {
      this.markerHeight = parseFloat(markerHeightValue)
    } else {
      this.markerHeight = 3
    }

    this.oldRefX = this.refX
    this.oldRefY = this.refY
    this.oldMarkerWidth = this.markerWidth
    this.oldMarkerHeight = this.markerHeight

    //init oryx attributes
    var optionalAttr = this.element.getAttributeNS(NAMESPACE_ORYX, 'optional')
    if (optionalAttr) {
      optionalAttr = optionalAttr.strip()
      this.optional = optionalAttr.toLowerCase() === 'yes'
    } else {
      this.optional = false
    }

    var enabledAttr = this.element.getAttributeNS(NAMESPACE_ORYX, 'enabled')
    if (enabledAttr) {
      enabledAttr = enabledAttr.strip()
      this.enabled = !(enabledAttr.toLowerCase() === 'no')
    } else {
      this.enabled = true
    }

    var minLengthAttr = this.element.getAttributeNS(NAMESPACE_ORYX, 'minimumLength')
    if (minLengthAttr) {
      this.minimumLength = parseFloat(minLengthAttr)
    }

    var resizeAttr = this.element.getAttributeNS(NAMESPACE_ORYX, 'resize')
    if (resizeAttr) {
      resizeAttr = resizeAttr.strip()
      this.resize = resizeAttr.toLowerCase() === 'yes'
    } else {
      this.resize = false
    }

    //init SVGShape objects
    //this.svgShapes = this._getSVGShapes(this.element);
  },

  /**
   *
   */
  _getSVGShapes: function(svgElement) {
    if (svgElement.hasChildNodes) {
      var svgShapes = []
      var me = this
      $A(svgElement.childNodes).each(function(svgChild) {
        try {
          var svgShape = new ORYX.Core.SVG.SVGShape(svgChild)
          svgShapes.push(svgShape)
        } catch (e) {
          svgShapes = svgShapes.concat(me._getSVGShapes(svgChild))
        }
      })
      return svgShapes
    }
  },

  /**
   * Writes the changed values into the SVG marker.
   */
  update: function() {
    //TODO mache marker resizebar!!! aber erst wenn der rest der connectingshape funzt!

    //		//update marker attributes
    //		if(this.refX != this.oldRefX) {
    //			this.element.setAttributeNS(null, "refX", this.refX);
    //		}
    //		if(this.refY != this.oldRefY) {
    //			this.element.setAttributeNS(null, "refY", this.refY);
    //		}
    //		if(this.markerWidth != this.oldMarkerWidth) {
    //			this.element.setAttributeNS(null, "markerWidth", this.markerWidth);
    //		}
    //		if(this.markerHeight != this.oldMarkerHeight) {
    //			this.element.setAttributeNS(null, "markerHeight", this.markerHeight);
    //		}
    //
    //		//update SVGShape objects
    //		var widthDelta = this.markerWidth / this.oldMarkerWidth;
    //		var heightDelta = this.markerHeight / this.oldMarkerHeight;
    //		if(widthDelta != 1 && heightDelta != 1) {
    //			this.svgShapes.each(function(svgShape) {
    //
    //			});
    //		}

    //update old values to prepare the next update
    this.oldRefX = this.refX
    this.oldRefY = this.refY
    this.oldMarkerWidth = this.markerWidth
    this.oldMarkerHeight = this.markerHeight
  },

  toString: function() {
    return this.element ? 'SVGMarker ' + this.element.id : 'SVGMarker ' + this.element
  }
})
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

/**
 *
 * Config variables
 */
NAMESPACE_ORYX = 'http://www.b3mn.org/oryx'
NAMESPACE_SVG = 'http://www.w3.org/2000/svg/'

/**
 * @classDescription This class wraps the manipulation of a SVG basic shape or a path.
 * @namespace ORYX.Core.SVG
 * uses Inheritance (Clazz)
 * uses Prototype 1.5.0
 * uses PathParser by Kevin Lindsey (http://kevlindev.com/)
 * uses MinMaxPathHandler
 * uses EditPathHandler
 *
 */

//init package
if (!ORYX) {
  var ORYX = {}
}
if (!ORYX.Core) {
  ORYX.Core = {}
}
if (!ORYX.Core.SVG) {
  ORYX.Core.SVG = {}
}

ORYX.Core.SVG.SVGShape = Clazz.extend({
  /**
   * Constructor
   * @param svgElem {SVGElement} An SVGElement that is a basic shape or a path.
   */
  construct: function(svgElem) {
    arguments.callee.$.construct.apply(this, arguments)

    this.type
    this.element = svgElem
    this.x = undefined
    this.y = undefined
    this.width = undefined
    this.height = undefined
    this.oldX = undefined
    this.oldY = undefined
    this.oldWidth = undefined
    this.oldHeight = undefined
    this.radiusX = undefined
    this.radiusY = undefined
    this.isHorizontallyResizable = false
    this.isVerticallyResizable = false
    //this.anchors = [];
    this.anchorLeft = false
    this.anchorRight = false
    this.anchorTop = false
    this.anchorBottom = false

    //attributes of path elements of edge objects
    this.allowDockers = true
    this.resizeMarkerMid = false

    this.editPathParser
    this.editPathHandler

    this.init() //initialisation of all the properties declared above.
  },

  /**
   * Initializes the values that are defined in the constructor.
   */
  init: function() {
    /**initialize position and size*/
    if (
      ORYX.Editor.checkClassType(this.element, SVGRectElement) ||
      ORYX.Editor.checkClassType(this.element, SVGImageElement)
    ) {
      this.type = 'Rect'

      var xAttr = this.element.getAttributeNS(null, 'x')
      if (xAttr) {
        this.oldX = parseFloat(xAttr)
      } else {
        throw 'Missing attribute in element ' + this.element
      }
      var yAttr = this.element.getAttributeNS(null, 'y')
      if (yAttr) {
        this.oldY = parseFloat(yAttr)
      } else {
        throw 'Missing attribute in element ' + this.element
      }
      var widthAttr = this.element.getAttributeNS(null, 'width')
      if (widthAttr) {
        this.oldWidth = parseFloat(widthAttr)
      } else {
        throw 'Missing attribute in element ' + this.element
      }
      var heightAttr = this.element.getAttributeNS(null, 'height')
      if (heightAttr) {
        this.oldHeight = parseFloat(heightAttr)
      } else {
        throw 'Missing attribute in element ' + this.element
      }
    } else if (ORYX.Editor.checkClassType(this.element, SVGCircleElement)) {
      this.type = 'Circle'

      var cx = undefined
      var cy = undefined
      //var r = undefined;

      var cxAttr = this.element.getAttributeNS(null, 'cx')
      if (cxAttr) {
        cx = parseFloat(cxAttr)
      } else {
        throw 'Missing attribute in element ' + this.element
      }
      var cyAttr = this.element.getAttributeNS(null, 'cy')
      if (cyAttr) {
        cy = parseFloat(cyAttr)
      } else {
        throw 'Missing attribute in element ' + this.element
      }
      var rAttr = this.element.getAttributeNS(null, 'r')
      if (rAttr) {
        //r = parseFloat(rAttr);
        this.radiusX = parseFloat(rAttr)
      } else {
        throw 'Missing attribute in element ' + this.element
      }
      this.oldX = cx - this.radiusX
      this.oldY = cy - this.radiusX
      this.oldWidth = 2 * this.radiusX
      this.oldHeight = 2 * this.radiusX
    } else if (ORYX.Editor.checkClassType(this.element, SVGEllipseElement)) {
      this.type = 'Ellipse'

      var cx = undefined
      var cy = undefined
      //var rx = undefined;
      //var ry = undefined;
      var cxAttr = this.element.getAttributeNS(null, 'cx')
      if (cxAttr) {
        cx = parseFloat(cxAttr)
      } else {
        throw 'Missing attribute in element ' + this.element
      }
      var cyAttr = this.element.getAttributeNS(null, 'cy')
      if (cyAttr) {
        cy = parseFloat(cyAttr)
      } else {
        throw 'Missing attribute in element ' + this.element
      }
      var rxAttr = this.element.getAttributeNS(null, 'rx')
      if (rxAttr) {
        this.radiusX = parseFloat(rxAttr)
      } else {
        throw 'Missing attribute in element ' + this.element
      }
      var ryAttr = this.element.getAttributeNS(null, 'ry')
      if (ryAttr) {
        this.radiusY = parseFloat(ryAttr)
      } else {
        throw 'Missing attribute in element ' + this.element
      }
      this.oldX = cx - this.radiusX
      this.oldY = cy - this.radiusY
      this.oldWidth = 2 * this.radiusX
      this.oldHeight = 2 * this.radiusY
    } else if (ORYX.Editor.checkClassType(this.element, SVGLineElement)) {
      this.type = 'Line'

      var x1 = undefined
      var y1 = undefined
      var x2 = undefined
      var y2 = undefined
      var x1Attr = this.element.getAttributeNS(null, 'x1')
      if (x1Attr) {
        x1 = parseFloat(x1Attr)
      } else {
        throw 'Missing attribute in element ' + this.element
      }
      var y1Attr = this.element.getAttributeNS(null, 'y1')
      if (y1Attr) {
        y1 = parseFloat(y1Attr)
      } else {
        throw 'Missing attribute in element ' + this.element
      }
      var x2Attr = this.element.getAttributeNS(null, 'x2')
      if (x2Attr) {
        x2 = parseFloat(x2Attr)
      } else {
        throw 'Missing attribute in element ' + this.element
      }
      var y2Attr = this.element.getAttributeNS(null, 'y2')
      if (y2Attr) {
        y2 = parseFloat(y2Attr)
      } else {
        throw 'Missing attribute in element ' + this.element
      }
      this.oldX = Math.min(x1, x2)
      this.oldY = Math.min(y1, y2)
      this.oldWidth = Math.abs(x1 - x2)
      this.oldHeight = Math.abs(y1 - y2)
    } else if (
      ORYX.Editor.checkClassType(this.element, SVGPolylineElement) ||
      ORYX.Editor.checkClassType(this.element, SVGPolygonElement)
    ) {
      this.type = 'Polyline'

      var pointsArray = []
      if (this.element.points && this.element.points.numberOfItems) {
        for (var i = 0, size = this.element.points.numberOfItems; i < size; i++) {
          pointsArray.push(this.element.points.getItem(i).x)
          pointsArray.push(this.element.points.getItem(i).y)
        }
      } else {
        var points = this.element.getAttributeNS(null, 'points')
        if (points) {
          points = points.replace(/,/g, ' ')
          pointsArray = points.split(' ')
          pointsArray = pointsArray.without('')
        } else {
          throw 'Missing attribute in element ' + this.element
        }
      }

      if (pointsArray && pointsArray.length && pointsArray.length > 1) {
        var minX = parseFloat(pointsArray[0])
        var minY = parseFloat(pointsArray[1])
        var maxX = parseFloat(pointsArray[0])
        var maxY = parseFloat(pointsArray[1])

        for (var i = 0; i < pointsArray.length; i++) {
          minX = Math.min(minX, parseFloat(pointsArray[i]))
          maxX = Math.max(maxX, parseFloat(pointsArray[i]))
          i++
          minY = Math.min(minY, parseFloat(pointsArray[i]))
          maxY = Math.max(maxY, parseFloat(pointsArray[i]))
        }

        this.oldX = minX
        this.oldY = minY
        this.oldWidth = maxX - minX
        this.oldHeight = maxY - minY
      } else {
        throw 'Missing attribute in element ' + this.element
      }
    } else if (ORYX.Editor.checkClassType(this.element, SVGPathElement)) {
      this.type = 'Path'

      this.editPathParser = new PathParser()
      this.editPathHandler = new ORYX.Core.SVG.EditPathHandler()
      this.editPathParser.setHandler(this.editPathHandler)

      var parser = new PathParser()
      var handler = new ORYX.Core.SVG.MinMaxPathHandler()
      parser.setHandler(handler)
      parser.parsePath(this.element)

      this.oldX = handler.minX
      this.oldY = handler.minY
      this.oldWidth = handler.maxX - handler.minX
      this.oldHeight = handler.maxY - handler.minY

      delete parser
      delete handler
    } else {
      throw 'Element is not a shape.'
    }

    /** initialize attributes of oryx namespace */
    //resize
    var resizeAttr = this.element.getAttributeNS(NAMESPACE_ORYX, 'resize')
    if (resizeAttr) {
      resizeAttr = resizeAttr.toLowerCase()
      if (resizeAttr.match(/horizontal/)) {
        this.isHorizontallyResizable = true
      } else {
        this.isHorizontallyResizable = false
      }
      if (resizeAttr.match(/vertical/)) {
        this.isVerticallyResizable = true
      } else {
        this.isVerticallyResizable = false
      }
    } else {
      this.isHorizontallyResizable = false
      this.isVerticallyResizable = false
    }

    //anchors
    var anchorAttr = this.element.getAttributeNS(NAMESPACE_ORYX, 'anchors')
    if (anchorAttr) {
      anchorAttr = anchorAttr.replace('/,/g', ' ')
      var anchors = anchorAttr.split(' ').without('')

      for (var i = 0; i < anchors.length; i++) {
        switch (anchors[i].toLowerCase()) {
          case 'left':
            this.anchorLeft = true
            break
          case 'right':
            this.anchorRight = true
            break
          case 'top':
            this.anchorTop = true
            break
          case 'bottom':
            this.anchorBottom = true
            break
        }
      }
    }

    //allowDockers and resizeMarkerMid
    if (ORYX.Editor.checkClassType(this.element, SVGPathElement)) {
      var allowDockersAttr = this.element.getAttributeNS(NAMESPACE_ORYX, 'allowDockers')
      if (allowDockersAttr) {
        if (allowDockersAttr.toLowerCase() === 'no') {
          this.allowDockers = false
        } else {
          this.allowDockers = true
        }
      }

      var resizeMarkerMidAttr = this.element.getAttributeNS(NAMESPACE_ORYX, 'resizeMarker-mid')
      if (resizeMarkerMidAttr) {
        if (resizeMarkerMidAttr.toLowerCase() === 'yes') {
          this.resizeMarkerMid = true
        } else {
          this.resizeMarkerMid = false
        }
      }
    }

    this.x = this.oldX
    this.y = this.oldY
    this.width = this.oldWidth
    this.height = this.oldHeight
  },

  /**
   * Writes the changed values into the SVG element.
   */
  update: function() {
    if (
      this.x !== this.oldX ||
      this.y !== this.oldY ||
      this.width !== this.oldWidth ||
      this.height !== this.oldHeight
    ) {
      switch (this.type) {
        case 'Rect':
          if (this.x !== this.oldX) this.element.setAttributeNS(null, 'x', this.x)
          if (this.y !== this.oldY) this.element.setAttributeNS(null, 'y', this.y)
          if (this.width !== this.oldWidth) this.element.setAttributeNS(null, 'width', this.width)
          if (this.height !== this.oldHeight) this.element.setAttributeNS(null, 'height', this.height)
          break
        case 'Circle':
          //calculate the radius
          //var r;
          //					if(this.width/this.oldWidth <= this.height/this.oldHeight) {
          //						this.radiusX = ((this.width > this.height) ? this.width : this.height)/2.0;
          //					} else {
          this.radiusX = (this.width < this.height ? this.width : this.height) / 2.0
          //}

          this.element.setAttributeNS(null, 'cx', this.x + this.width / 2.0)
          this.element.setAttributeNS(null, 'cy', this.y + this.height / 2.0)
          this.element.setAttributeNS(null, 'r', this.radiusX)
          break
        case 'Ellipse':
          this.radiusX = this.width / 2
          this.radiusY = this.height / 2

          this.element.setAttributeNS(null, 'cx', this.x + this.radiusX)
          this.element.setAttributeNS(null, 'cy', this.y + this.radiusY)
          this.element.setAttributeNS(null, 'rx', this.radiusX)
          this.element.setAttributeNS(null, 'ry', this.radiusY)
          break
        case 'Line':
          if (this.x !== this.oldX) this.element.setAttributeNS(null, 'x1', this.x)

          if (this.y !== this.oldY) this.element.setAttributeNS(null, 'y1', this.y)

          if (this.x !== this.oldX || this.width !== this.oldWidth)
            this.element.setAttributeNS(null, 'x2', this.x + this.width)

          if (this.y !== this.oldY || this.height !== this.oldHeight)
            this.element.setAttributeNS(null, 'y2', this.y + this.height)
          break
        case 'Polyline':
          var points = this.element.getAttributeNS(null, 'points')
          if (points) {
            points = points
              .replace(/,/g, ' ')
              .split(' ')
              .without('')

            if (points && points.length && points.length > 1) {
              //TODO what if oldWidth == 0?
              var widthDelta = this.oldWidth === 0 ? 0 : this.width / this.oldWidth
              var heightDelta = this.oldHeight === 0 ? 0 : this.height / this.oldHeight

              var updatedPoints = ''
              for (var i = 0; i < points.length; i++) {
                var x = (parseFloat(points[i]) - this.oldX) * widthDelta + this.x
                i++
                var y = (parseFloat(points[i]) - this.oldY) * heightDelta + this.y
                updatedPoints += x + ' ' + y + ' '
              }
              this.element.setAttributeNS(null, 'points', updatedPoints)
            } else {
              //TODO error
            }
          } else {
            //TODO error
          }
          break
        case 'Path':
          //calculate scaling delta
          //TODO what if oldWidth == 0?
          var widthDelta = this.oldWidth === 0 ? 0 : this.width / this.oldWidth
          var heightDelta = this.oldHeight === 0 ? 0 : this.height / this.oldHeight

          //use path parser to edit each point of the path
          this.editPathHandler.init(this.x, this.y, this.oldX, this.oldY, widthDelta, heightDelta)
          this.editPathParser.parsePath(this.element)

          //change d attribute of path
          this.element.setAttributeNS(null, 'd', this.editPathHandler.d)
          break
      }

      this.oldX = this.x
      this.oldY = this.y
      this.oldWidth = this.width
      this.oldHeight = this.height
    }

    // Remove cached variables
    delete this.visible
    delete this.handler
  },

  isPointIncluded: function(pointX, pointY) {
    // Check if there are the right arguments and if the node is visible
    if (!pointX || !pointY || !this.isVisible()) {
      return false
    }

    switch (this.type) {
      case 'Rect':
        return pointX >= this.x && pointX <= this.x + this.width && pointY >= this.y && pointY <= this.y + this.height
        break
      case 'Circle':
        //calculate the radius
        //				var r;
        //				if(this.width/this.oldWidth <= this.height/this.oldHeight) {
        //					r = ((this.width > this.height) ? this.width : this.height)/2.0;
        //				} else {
        //				 	r = ((this.width < this.height) ? this.width : this.height)/2.0;
        //				}
        return ORYX.Core.Math.isPointInEllipse(
          pointX,
          pointY,
          this.x + this.width / 2.0,
          this.y + this.height / 2.0,
          this.radiusX,
          this.radiusX
        )
        break
      case 'Ellipse':
        return ORYX.Core.Math.isPointInEllipse(
          pointX,
          pointY,
          this.x + this.radiusX,
          this.y + this.radiusY,
          this.radiusX,
          this.radiusY
        )
        break
      case 'Line':
        return ORYX.Core.Math.isPointInLine(pointX, pointY, this.x, this.y, this.x + this.width, this.y + this.height)
        break
      case 'Polyline':
        var points = this.element.getAttributeNS(null, 'points')

        if (points) {
          points = points
            .replace(/,/g, ' ')
            .split(' ')
            .without('')

          points = points.collect(function(n) {
            return parseFloat(n)
          })

          return ORYX.Core.Math.isPointInPolygone(pointX, pointY, points)
        } else {
          return false
        }
        break
      case 'Path':
        // Cache Path handler
        if (!this.handler) {
          var parser = new PathParser()
          this.handler = new ORYX.Core.SVG.PointsPathHandler()
          parser.setHandler(this.handler)
          parser.parsePath(this.element)
        }

        return ORYX.Core.Math.isPointInPolygone(pointX, pointY, this.handler.points)

        break
      default:
        return false
    }
  },

  /**
   * Returns true if the element is visible
   * @param {SVGElement} elem
   * @return boolean
   */
  isVisible: function(elem) {
    if (this.visible !== undefined) {
      return this.visible
    }

    if (!elem) {
      elem = this.element
    }

    var hasOwnerSVG = false
    try {
      hasOwnerSVG = !!elem.ownerSVGElement
    } catch (e) {}

    // Is SVG context
    if (hasOwnerSVG) {
      // IF G-Element
      if (ORYX.Editor.checkClassType(elem, SVGGElement)) {
        if (elem.className && elem.className.baseVal == 'me') {
          this.visible = true
          return this.visible
        }
      }

      // Check if fill or stroke is set
      var fill = elem.getAttributeNS(null, 'fill')
      var stroke = elem.getAttributeNS(null, 'stroke')
      if (fill && fill == 'none' && stroke && stroke == 'none') {
        this.visible = false
      } else {
        // Check if displayed
        var attr = elem.getAttributeNS(null, 'display')
        if (!attr) this.visible = this.isVisible(elem.parentNode)
        else if (attr == 'none') this.visible = false
        else this.visible = true
      }
    } else {
      this.visible = true
    }

    return this.visible
  },

  toString: function() {
    return this.element ? 'SVGShape ' + this.element.id : 'SVGShape ' + this.element
  }
})
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

/**
 * Init namespaces
 */
if (!ORYX) {
  var ORYX = {}
}
if (!ORYX.Core) {
  ORYX.Core = {}
}
if (!ORYX.Core.SVG) {
  ORYX.Core.SVG = {}
}

/**
 * @classDescription Class for adding text to a shape.
 *
 */
ORYX.Core.SVG.Label = Clazz.extend({
  _characterSets: [
    '%W',
    '@',
    'm',
    'wDGMOQ√ñ#+=<>~^',
    'ABCHKNRSUVXZ√ú√Ñ&',
    'bdghnopqux√∂√ºETY1234567890√ü_¬ß${}*¬¥`¬µ‚Ç¨',
    'aeksvyz√§FLP?¬∞¬≤¬≥',
    'c-',
    'rtJ"/()[]:;!|\\',
    'fjI., ',
    "'",
    'il'
  ],
  _characterSetValues: [15, 14, 13, 11, 10, 9, 8, 7, 6, 5, 4, 3],

  /**
   * Constructor
   * @param options {Object} :
   * 	textElement
   *
   */
  construct: function(options) {
    arguments.callee.$.construct.apply(this, arguments)

    if (!options.textElement) {
      throw 'Label: No parameter textElement.'
    } else if (!ORYX.Editor.checkClassType(options.textElement, SVGTextElement)) {
      throw 'Label: Parameter textElement is not an SVGTextElement.'
    }

    this.invisibleRenderPoint = -5000

    this.node = options.textElement

    this.node.setAttributeNS(null, 'stroke-width', '0pt')
    this.node.setAttributeNS(null, 'letter-spacing', '0px')

    this.shapeId = options.shapeId

    this.id

    this.fitToElemId

    this.edgePosition

    this.x
    this.y
    this.oldX
    this.oldY

    this.isVisible = true

    this._text
    this._verticalAlign
    this._horizontalAlign
    this._rotate
    this._rotationPoint

    //this.anchors = [];
    this.anchorLeft
    this.anchorRight
    this.anchorTop
    this.anchorBottom

    this._isChanged = true

    //if the text element already has an id, don't change it.
    var _id = this.node.getAttributeNS(null, 'id')
    if (_id) {
      this.id = _id
    }

    //initialization

    //set referenced element the text is fit to
    this.fitToElemId = this.node.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'fittoelem')
    if (this.fitToElemId) this.fitToElemId = this.shapeId + this.fitToElemId

    //set alignment
    var alignValues = this.node.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'align')
    if (alignValues) {
      alignValues = alignValues.replace(/,/g, ' ')
      alignValues = alignValues.split(' ')
      alignValues = alignValues.without('')

      alignValues.each(
        function(alignValue) {
          switch (alignValue) {
            case 'top':
            case 'middle':
            case 'bottom':
              if (!this._verticalAlign) {
                this._originVerticalAlign = this._verticalAlign = alignValue
              }
              break
            case 'left':
            case 'center':
            case 'right':
              if (!this._horizontalAlign) {
                this._originHorizontalAlign = this._horizontalAlign = alignValue
              }
              break
          }
        }.bind(this)
      )
    }

    //set edge position (only in case the label belongs to an edge)
    this.edgePosition = this.node.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'edgePosition')
    if (this.edgePosition) {
      this.originEdgePosition = this.edgePosition = this.edgePosition.toLowerCase()
    }

    //get offset top
    this.offsetTop =
      this.node.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'offsetTop') || ORYX.CONFIG.OFFSET_EDGE_LABEL_TOP
    if (this.offsetTop) {
      this.offsetTop = parseInt(this.offsetTop)
    }

    //get offset top
    this.offsetBottom =
      this.node.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'offsetBottom') || ORYX.CONFIG.OFFSET_EDGE_LABEL_BOTTOM
    if (this.offsetBottom) {
      this.offsetBottom = parseInt(this.offsetBottom)
    }

    //set rotation
    var rotateValue = this.node.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'rotate')
    if (rotateValue) {
      try {
        this._rotate = parseFloat(rotateValue)
      } catch (e) {
        this._rotate = 0
      }
    } else {
      this._rotate = 0
    }

    //anchors
    var anchorAttr = this.node.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'anchors')
    if (anchorAttr) {
      anchorAttr = anchorAttr.replace('/,/g', ' ')
      var anchors = anchorAttr.split(' ').without('')

      for (var i = 0; i < anchors.length; i++) {
        switch (anchors[i].toLowerCase()) {
          case 'left':
            this.originAnchorLeft = this.anchorLeft = true
            break
          case 'right':
            this.originAnchorRight = this.anchorRight = true
            break
          case 'top':
            this.originAnchorTop = this.anchorTop = true
            break
          case 'bottom':
            this.originAnchorBottom = this.anchorBottom = true
            break
        }
      }
    }

    //if no alignment defined, set default alignment
    if (!this._verticalAlign) {
      this._verticalAlign = 'bottom'
    }
    if (!this._horizontalAlign) {
      this._horizontalAlign = 'left'
    }

    var xValue = this.node.getAttributeNS(null, 'x')
    if (xValue) {
      this.oldX = this.x = parseFloat(xValue)
    } else {
      //TODO error
    }

    var yValue = this.node.getAttributeNS(null, 'y')
    if (yValue) {
      this.oldY = this.y = parseFloat(yValue)
    } else {
      //TODO error
    }

    //set initial text
    this.text(this.node.textContent)
  },

  /**
   * Reset the anchor position to the original value
   * which was specified in the stencil set
   *
   */
  resetAnchorPosition: function() {
    this.anchorLeft = this.originAnchorLeft || false
    this.anchorRight = this.originAnchorRight || false
    this.anchorTop = this.originAnchorTop || false
    this.anchorBottom = this.originAnchorBottom || false
  },

  isOriginAnchorLeft: function() {
    return this.originAnchorLeft || false
  },
  isOriginAnchorRight: function() {
    return this.originAnchorRight || false
  },
  isOriginAnchorTop: function() {
    return this.originAnchorTop || false
  },
  isOriginAnchorBottom: function() {
    return this.originAnchorBottom || false
  },

  isAnchorLeft: function() {
    return this.anchorLeft || false
  },
  isAnchorRight: function() {
    return this.anchorRight || false
  },
  isAnchorTop: function() {
    return this.anchorTop || false
  },
  isAnchorBottom: function() {
    return this.anchorBottom || false
  },

  /**
   * Returns the x coordinate
   * @return {number}
   */
  getX: function() {
    try {
      var x = this.node.x.baseVal.getItem(0).value
      switch (this.horizontalAlign()) {
        case 'left':
          return x
        case 'center':
          return x - this.getWidth() / 2
        case 'right':
          return x - this.getWidth()
      }
      return this.node.getBBox().x
    } catch (e) {
      return this.x
    }
  },

  setX: function(x) {
    if (this.position) this.position.x = x
    else this.setOriginX(x)
  },

  /**
   * Returns the y coordinate
   * @return {number}
   */
  getY: function() {
    try {
      return this.node.getBBox().y
    } catch (e) {
      return this.y
    }
  },

  setY: function(y) {
    if (this.position) this.position.y = y
    else this.setOriginY(y)
  },

  setOriginX: function(x) {
    this.x = x
  },

  setOriginY: function(y) {
    this.y = y
  },

  /**
   * Returns the width of the label
   * @return {number}
   */
  getWidth: function() {
    try {
      try {
        var width,
          cn = this.node.childNodes
        if (cn.length == 0) {
          width = this.node.getBBox().width
        } else {
          for (var i = 0, size = cn.length; i < size; ++i) {
            var w = cn[i].getComputedTextLength()
            if ('undefined' == typeof width || width < w) {
              width = w
            }
          }
        }
        return width + (width % 2 == 0 ? 0 : 1)
      } catch (ee) {
        return this.node.getBBox().width
      }
    } catch (e) {
      return 0
    }
  },

  getOriginUpperLeft: function() {
    var x = this.x,
      y = this.y
    switch (this._horizontalAlign) {
      case 'center':
        x -= this.getWidth() / 2
        break
      case 'right':
        x -= this.getWidth()
        break
    }
    switch (this._verticalAlign) {
      case 'middle':
        y -= this.getHeight() / 2
        break
      case 'bottom':
        y -= this.getHeight()
        break
    }
    return { x: x, y: y }
  },

  /**
   * Returns the height of the label
   * @return {number}
   */
  getHeight: function() {
    try {
      return this.node.getBBox().height
    } catch (e) {
      return 0
    }
  },

  /**
   * Returns the relative center position of the label
   * to its parent shape.
   * @return {Object}
   */
  getCenter: function() {
    var up = { x: this.getX(), y: this.getY() }
    up.x += this.getWidth() / 2
    up.y += this.getHeight() / 2
    return up
  },

  /**
   * Sets the position of a label relative to the parent.
   * @param {Object} position
   */
  setPosition: function(position) {
    if (!position || position.x === undefined || position.y === undefined) {
      delete this.position
    } else {
      this.position = position
    }

    if (this.position) {
      delete this._referencePoint
      delete this.edgePosition
    }

    this._isChanged = true
    this.update()
  },

  /**
   * Return the position
   */
  getPosition: function() {
    return this.position
  },

  setReferencePoint: function(ref) {
    if (ref) {
      this._referencePoint = ref
    } else {
      delete this._referencePoint
    }
    if (this._referencePoint) {
      delete this.position
    }
  },

  getReferencePoint: function() {
    return this._referencePoint || undefined
  },

  changed: function() {
    this._isChanged = true
  },

  /**
   * Register a callback which will be called if the label
   * was rendered.
   * @param {Object} fn
   */
  registerOnChange: function(fn) {
    if (!this.changeCallbacks) {
      this.changeCallbacks = []
    }
    if (fn instanceof Function && !this.changeCallbacks.include(fn)) {
      this.changeCallbacks.push(fn)
    }
  },

  /**
   * Unregister the callback for changes.
   * @param {Object} fn
   */
  unregisterOnChange: function(fn) {
    if (this.changeCallbacks && fn instanceof Function && this.changeCallbacks.include(fn)) {
      this.changeCallbacks = this.changeCallbacks.without(fn)
    }
  },

  /**
   * Returns TRUE if the labe is currently in
   * the update mechanism.
   * @return {Boolean}
   */
  isUpdating: function() {
    return !!this._isUpdating
  },

  getOriginEdgePosition: function() {
    return this.originEdgePosition
  },

  /**
   * Returns the edgeposition.
   *
   * @return {String} "starttop", "startmiddle", "startbottom",
   * "midtop", "midbottom", "endtop", "endbottom" or null
   */
  getEdgePosition: function() {
    return this.edgePosition || null
  },

  /**
   * Set the edge position, must be one of the valid
   * edge positions (see getEdgePosition).
   * Removes the reference point and the absolute position as well.
   *
   * @param {Object} position
   */
  setEdgePosition: function(position) {
    if (['starttop', 'startmiddle', 'startbottom', 'midtop', 'midbottom', 'endtop', 'endbottom'].include(position)) {
      this.edgePosition = position
      delete this.position
      delete this._referencePoint
    } else {
      delete this.edgePosition
    }
  },

  /**
   * Update the SVG text element.
   */
  update: function(force) {
    var x = this.x,
      y = this.y
    if (this.position) {
      x = this.position.x
      y = this.position.y
    }
    x = Math.floor(x)
    y = Math.floor(y)

    if (this._isChanged || x !== this.oldX || y !== this.oldY || force === true) {
      if (this.isVisible) {
        this._isChanged = false
        this._isUpdating = true

        this.node.setAttributeNS(null, 'x', x)
        this.node.setAttributeNS(null, 'y', y)
        this.node.removeAttributeNS(null, 'fill-opacity')

        //this.node.setAttributeNS(null, 'font-size', this._fontSize);
        //this.node.setAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'align', this._horizontalAlign + " " + this._verticalAlign);

        this.oldX = x
        this.oldY = y

        //set rotation
        if (!this.position && !this.getReferencePoint()) {
          if (this._rotate !== undefined) {
            if (this._rotationPoint)
              this.node.setAttributeNS(
                null,
                'transform',
                'rotate(' +
                  this._rotate +
                  ' ' +
                  Math.floor(this._rotationPoint.x) +
                  ' ' +
                  Math.floor(this._rotationPoint.y) +
                  ')'
              )
            else
              this.node.setAttributeNS(
                null,
                'transform',
                'rotate(' + this._rotate + ' ' + Math.floor(x) + ' ' + Math.floor(y) + ')'
              )
          }
        } else {
          this.node.removeAttributeNS(null, 'transform')
        }

        var textLines = this._text.split('\n')
        while (textLines.last() == '') textLines.pop()

        if (this.node.ownerDocument) {
          // Only reset the tspans if the text
          // has changed or has to be wrapped
          if (this.fitToElemId || this._textHasChanged) {
            this.node.textContent = '' // Remove content
            textLines.each(
              function(textLine, index) {
                var tspan = this.node.ownerDocument.createElementNS(ORYX.CONFIG.NAMESPACE_SVG, 'tspan')
                tspan.textContent = textLine.trim()
                if (this.fitToElemId) {
                  tspan.setAttributeNS(null, 'x', this.invisibleRenderPoint)
                  tspan.setAttributeNS(null, 'y', this.invisibleRenderPoint)
                }

                /*
                 * Chrome's getBBox() method fails, if a text node contains an empty tspan element.
                 * So, we add a whitespace to such a tspan element.
                 */
                if (tspan.textContent === '') {
                  tspan.textContent = ' '
                }

                //append tspan to text node
                this.node.appendChild(tspan)
              }.bind(this)
            )
            delete this._textHasChanged
            delete this.indices
          }

          //Work around for Mozilla bug 293581
          if (this.isVisible && this.fitToElemId) {
            this.node.setAttributeNS(null, 'visibility', 'hidden')
          }

          if (this.fitToElemId) {
            window.setTimeout(this._checkFittingToReferencedElem.bind(this), 0)
            //this._checkFittingToReferencedElem();
          } else {
            window.setTimeout(this._positionText.bind(this), 0)
            //this._positionText();
          }
        }
      } else {
        this.node.textContent = ''
        //this.node.setAttributeNS(null, "fill-opacity", "0.2");
      }
    }
  },

  _checkFittingToReferencedElem: function() {
    try {
      var tspans = $A(this.node.getElementsByTagNameNS(ORYX.CONFIG.NAMESPACE_SVG, 'tspan'))

      //only do this in firefox 3. all other browsers do not support word wrapping!!!!!
      //if (/Firefox[\/\s](\d+\.\d+)/.test(navigator.userAgent) && new Number(RegExp.$1)>=3) {
      var newtspans = []

      var refNode = this.node.ownerDocument.getElementById(this.fitToElemId)

      if (refNode) {
        var refbb = refNode.getBBox()

        var fontSize = this.getFontSize()

        for (var j = 0; j < tspans.length; j++) {
          var tspan = tspans[j]

          var textLength = this._getRenderedTextLength(tspan, undefined, undefined, fontSize)

          var refBoxLength =
            this._rotate != 0 && this._rotate % 180 != 0 && this._rotate % 90 == 0 ? refbb.height : refbb.width

          if (textLength > refBoxLength) {
            var startIndex = 0
            var lastSeperatorIndex = 0

            var numOfChars = this.getTrimmedTextLength(tspan.textContent)
            for (var i = 0; i < numOfChars; i++) {
              var sslength = this._getRenderedTextLength(tspan, startIndex, i - startIndex, fontSize)

              if (sslength > refBoxLength - 2) {
                var newtspan = this.node.ownerDocument.createElementNS(ORYX.CONFIG.NAMESPACE_SVG, 'tspan')
                if (lastSeperatorIndex <= startIndex) {
                  lastSeperatorIndex = i == 0 ? i : i - 1
                  newtspan.textContent = tspan.textContent.slice(startIndex, lastSeperatorIndex).trim()
                  //lastSeperatorIndex = i;
                } else {
                  newtspan.textContent = tspan.textContent.slice(startIndex, ++lastSeperatorIndex).trim()
                }

                newtspan.setAttributeNS(null, 'x', this.invisibleRenderPoint)
                newtspan.setAttributeNS(null, 'y', this.invisibleRenderPoint)

                //insert tspan to text node
                //this.node.insertBefore(newtspan, tspan);
                newtspans.push(newtspan)

                startIndex = lastSeperatorIndex
              } else {
                var curChar = tspan.textContent.charAt(i)
                if (
                  curChar == ' ' ||
                  curChar == '-' ||
                  curChar == '.' ||
                  curChar == ',' ||
                  curChar == ';' ||
                  curChar == ':'
                ) {
                  lastSeperatorIndex = i
                }
              }
            }

            tspan.textContent = tspan.textContent.slice(startIndex).trim()
          }

          newtspans.push(tspan)
        }

        while (this.node.hasChildNodes()) this.node.removeChild(this.node.childNodes[0])

        while (newtspans.length > 0) {
          this.node.appendChild(newtspans.shift())
        }
      }
      //}
    } catch (e) {
      ORYX.Log.fatal('Error ' + e)
    }
    window.setTimeout(this._positionText.bind(this), 0)
    //this._positionText();
  },

  /**
   * This is a work around method for Mozilla bug 293581.
   * Before the method getComputedTextLength works, the text has to be rendered.
   */
  _positionText: function() {
    try {
      var tspans = this.node.childNodes

      var fontSize = this.getFontSize(this.node)

      var invalidTSpans = []

      var x = this.x,
        y = this.y
      if (this.position) {
        x = this.position.x
        y = this.position.y
      }
      x = Math.floor(x)
      y = Math.floor(y)

      var i = 0,
        indic = [] // Cache indices if the _positionText is called again, before update is called
      var is = this.indices || $R(0, tspans.length - 1).toArray()
      var length = is.length
      is.each(
        function(index) {
          if ('undefined' == typeof index) {
            return
          }

          var tspan = tspans[i++]

          if (tspan.textContent.trim() === '') {
            invalidTSpans.push(tspan)
          } else {
            //set vertical position
            var dy = 0
            switch (this._verticalAlign) {
              case 'bottom':
                dy = -(length - index - 1) * fontSize
                break
              case 'middle':
                dy = -(length / 2.0 - index - 1) * fontSize
                dy -= ORYX.CONFIG.LABEL_LINE_DISTANCE / 2
                break
              case 'top':
                dy = index * fontSize
                dy += fontSize
                break
            }
            tspan.setAttributeNS(null, 'dy', Math.floor(dy))

            tspan.setAttributeNS(null, 'x', x)
            tspan.setAttributeNS(null, 'y', y)
            indic.push(index)
          }
        }.bind(this)
      )

      indic.length = tspans.length
      this.indices = this.indices || indic

      invalidTSpans.each(
        function(tspan) {
          this.node.removeChild(tspan)
        }.bind(this)
      )

      //set horizontal alignment
      switch (this._horizontalAlign) {
        case 'left':
          this.node.setAttributeNS(null, 'text-anchor', 'start')
          break
        case 'center':
          this.node.setAttributeNS(null, 'text-anchor', 'middle')
          break
        case 'right':
          this.node.setAttributeNS(null, 'text-anchor', 'end')
          break
      }
    } catch (e) {
      //console.log(e);
      this._isChanged = true
    }

    if (this.isVisible) {
      this.node.removeAttributeNS(null, 'visibility')
    }

    // Finished
    delete this._isUpdating

    // Raise change event
    ;(this.changeCallbacks || []).each(function(fn) {
      fn.apply(fn)
    })
  },

  /**
   * Returns the text length of the text content of an SVG tspan element.
   * For all browsers but Firefox 3 the values are estimated.
   * @param {TSpanSVGElement} tspan
   * @param {int} startIndex Optional, for sub strings
   * @param {int} endIndex Optional, for sub strings
   */
  _getRenderedTextLength: function(tspan, startIndex, endIndex, fontSize) {
    //if (/Firefox[\/\s](\d+\.\d+)/.test(navigator.userAgent) && new Number(RegExp.$1) >= 3) {
    if (startIndex === undefined) {
      //test string: abcdefghijklmnopqrstuvwxyz√∂√§√º,.-#+ 1234567890√üABCDEFGHIJKLMNOPQRSTUVWXYZ;:_'*√ú√Ñ√ñ!"¬ß$%&/()=?[]{}|<>'~¬¥`\^¬∞¬µ@‚Ç¨¬≤¬≥
      //				for(var i = 0; i < tspan.textContent.length; i++) {
      //					console.log(tspan.textContent.charAt(i), tspan.getSubStringLength(i,1), this._estimateCharacterWidth(tspan.textContent.charAt(i))*(fontSize/14.0));
      //				}
      return tspan.getComputedTextLength()
    } else {
      return tspan.getSubStringLength(startIndex, endIndex)
    }
    /*} else {
			if(startIndex === undefined) {
				return this._estimateTextWidth(tspan.textContent, fontSize);
			} else {
				return this._estimateTextWidth(tspan.textContent.substr(startIndex, endIndex).trim(), fontSize);
			}
		}*/
  },

  /**
   * Estimates the text width for a string.
   * Used for word wrapping in all browser but FF3.
   * @param {Object} text
   */
  _estimateTextWidth: function(text, fontSize) {
    var sum = 0.0
    for (var i = 0; i < text.length; i++) {
      sum += this._estimateCharacterWidth(text.charAt(i))
    }

    return sum * (fontSize / 14.0)
  },

  /**
   * Estimates the width of a single character for font size 14.
   * Used for word wrapping in all browser but FF3.
   * @param {Object} character
   */
  _estimateCharacterWidth: function(character) {
    for (var i = 0; i < this._characterSets.length; i++) {
      if (this._characterSets[i].indexOf(character) >= 0) {
        return this._characterSetValues[i]
      }
    }
    return 9
  },

  getReferencedElementWidth: function() {
    var refNode = this.node.ownerDocument.getElementById(this.fitToElemId)

    if (refNode) {
      var refbb = refNode.getBBox()

      if (refbb) {
        return this._rotate != 0 && this._rotate % 180 != 0 && this._rotate % 90 == 0 ? refbb.height : refbb.width
      }
    }

    return undefined
  },

  /**
   * If no parameter is provided, this method returns the current text.
   * @param text {String} Optional. Replaces the old text with this one.
   */
  text: function() {
    switch (arguments.length) {
      case 0:
        return this._text
        break

      case 1:
        var oldText = this._text
        if (arguments[0]) {
          // Filter out multiple spaces to fix issue in chrome for line-wrapping
          this._text = arguments[0].toString()
          if (this._text != null && this._text != undefined) {
            this._text = this._text.replace(/ {2,}/g, ' ')
          }
        } else {
          this._text = ''
        }
        if (oldText !== this._text) {
          this._isChanged = true
          this._textHasChanged = true
        }
        break

      default:
        //TODO error
        break
    }
  },

  getOriginVerticalAlign: function() {
    return this._originVerticalAlign
  },

  verticalAlign: function() {
    switch (arguments.length) {
      case 0:
        return this._verticalAlign
      case 1:
        if (['top', 'middle', 'bottom'].member(arguments[0])) {
          var oldValue = this._verticalAlign
          this._verticalAlign = arguments[0]
          if (this._verticalAlign !== oldValue) {
            this._isChanged = true
          }
        }
        break

      default:
        //TODO error
        break
    }
  },

  getOriginHorizontalAlign: function() {
    return this._originHorizontalAlign
  },

  horizontalAlign: function() {
    switch (arguments.length) {
      case 0:
        return this._horizontalAlign
      case 1:
        if (['left', 'center', 'right'].member(arguments[0])) {
          var oldValue = this._horizontalAlign
          this._horizontalAlign = arguments[0]
          if (this._horizontalAlign !== oldValue) {
            this._isChanged = true
          }
        }
        break

      default:
        //TODO error
        break
    }
  },

  rotate: function() {
    switch (arguments.length) {
      case 0:
        return this._rotate
      case 1:
        if (this._rotate != arguments[0]) {
          this._rotate = arguments[0]
          this._rotationPoint = undefined
          this._isChanged = true
        }
      case 2:
        if (
          this._rotate != arguments[0] ||
          !this._rotationPoint ||
          this._rotationPoint.x != arguments[1].x ||
          this._rotationPoint.y != arguments[1].y
        ) {
          this._rotate = arguments[0]
          this._rotationPoint = arguments[1]
          this._isChanged = true
        }
    }
  },

  hide: function() {
    if (this.isVisible) {
      this.isVisible = false
      this._isChanged = true
    }
  },

  show: function() {
    if (!this.isVisible) {
      this.isVisible = true
      this._isChanged = true

      // Since text is removed from the tspan when "hidden", mark
      // the text as changed to get it redrawn
      this._textHasChanged = true
    }
  },

  /**
   * iterates parent nodes till it finds a SVG font-size
   * attribute.
   * @param {SVGElement} node
   */
  getInheritedFontSize: function(node) {
    if (!node || !node.getAttributeNS) return

    var attr = node.getAttributeNS(null, 'font-size')
    if (attr) {
      return parseFloat(attr)
    } else if (!ORYX.Editor.checkClassType(node, SVGSVGElement)) {
      return this.getInheritedFontSize(node.parentNode)
    }
  },

  getFontSize: function(node) {
    var tspans = this.node.getElementsByTagNameNS(ORYX.CONFIG.NAMESPACE_SVG, 'tspan')

    //trying to get an inherited font-size attribute
    //NO CSS CONSIDERED!
    var fontSize = this.getInheritedFontSize(this.node)

    if (!fontSize) {
      //because this only works in firefox 3, all other browser use the default line height
      if (tspans[0] && /Firefox[\/\s](\d+\.\d+)/.test(navigator.userAgent) && new Number(RegExp.$1) >= 3) {
        fontSize = tspans[0].getExtentOfChar(0).height
      } else {
        fontSize = ORYX.CONFIG.LABEL_DEFAULT_LINE_HEIGHT
      }

      //handling of unsupported method in webkit
      if (fontSize <= 0) {
        fontSize = ORYX.CONFIG.LABEL_DEFAULT_LINE_HEIGHT
      }
    }

    if (fontSize) this.node.setAttribute('oryx:fontSize', fontSize)

    return fontSize
  },

  /**
   * Get trimmed text length for use with
   * getExtentOfChar and getSubStringLength.
   * @param {String} text
   */
  getTrimmedTextLength: function(text) {
    text = text.strip().gsub('  ', ' ')

    var oldLength
    do {
      oldLength = text.length
      text = text.gsub('  ', ' ')
    } while (oldLength > text.length)

    return text.length
  },

  /**
   * Returns the offset from
   * edge to the label which is
   * positioned under the edge
   * @return {int}
   */
  getOffsetBottom: function() {
    return this.offsetBottom
  },

  /**
   * Returns the offset from
   * edge to the label which is
   * positioned over the edge
   * @return {int}
   */
  getOffsetTop: function() {
    return this.offsetTop
  },

  /**
   *
   * @param {Object} obj
   */
  deserialize: function(obj, shape) {
    if (obj && 'undefined' != typeof obj.x && 'undefined' != typeof obj.y) {
      this.setPosition({ x: obj.x, y: obj.y })

      if ('undefined' != typeof obj.distance) {
        var from = shape.dockers[obj.from]
        var to = shape.dockers[obj.to]
        if (from && to) {
          this.setReferencePoint({
            dirty: true,
            distance: obj.distance,
            intersection: { x: obj.x, y: obj.y },
            orientation: obj.orientation,
            segment: {
              from: from,
              fromIndex: obj.from,
              fromPosition: from.bounds.center(),
              to: to,
              toIndex: obj.to,
              toPosition: to.bounds.center()
            }
          })
        }
      }

      if (obj.left) this.anchorLeft = true
      if (obj.right) this.anchorRight = true
      if (obj.top) this.anchorTop = true
      if (obj.bottom) this.anchorBottom = true
      if (obj.valign) this.verticalAlign(obj.valign)
      if (obj.align) this.horizontalAlign(obj.align)
    } else if (obj && 'undefined' != typeof obj.edge) {
      this.setEdgePosition(obj.edge)
    }
  },

  /**
   *
   * @return {Object}
   */
  serialize: function() {
    // On edge position
    if (this.getEdgePosition()) {
      if (this.getOriginEdgePosition() !== this.getEdgePosition()) {
        return { edge: this.getEdgePosition() }
      } else {
        return null
      }
    }

    // On self defined position
    if (this.position) {
      var pos = { x: this.position.x, y: this.position.y }
      if (this.isAnchorLeft() && this.isAnchorLeft() !== this.isOriginAnchorLeft()) {
        pos.left = true
      }
      if (this.isAnchorRight() && this.isAnchorRight() !== this.isOriginAnchorRight()) {
        pos.right = true
      }
      if (this.isAnchorTop() && this.isAnchorTop() !== this.isOriginAnchorTop()) {
        pos.top = true
      }
      if (this.isAnchorBottom() && this.isAnchorBottom() !== this.isOriginAnchorBottom()) {
        pos.bottom = true
      }

      if (this.getOriginVerticalAlign() !== this.verticalAlign()) {
        pos.valign = this.verticalAlign()
      }
      if (this.getOriginHorizontalAlign() !== this.horizontalAlign()) {
        pos.align = this.horizontalAlign()
      }

      return pos
    }

    // On reference point which is interesting for edges
    if (this.getReferencePoint()) {
      var ref = this.getReferencePoint()
      return {
        distance: ref.distance,
        x: ref.intersection.x,
        y: ref.intersection.y,
        from: ref.segment.fromIndex,
        to: ref.segment.toIndex,
        orientation: ref.orientation,
        valign: this.verticalAlign(),
        align: this.horizontalAlign()
      }
    }
    return null
  },

  toString: function() {
    return 'Label ' + this.id
  }
})
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

/**
 * Init namespaces
 */
if (!ORYX) {
  var ORYX = {}
}
if (!ORYX.Core) {
  ORYX.Core = {}
}
if (!ORYX.Core.Math) {
  ORYX.Core.Math = {}
}

/**
 * Calculate the middle point between two given points
 * @param {x:double, y:double} point1
 * @param {x:double, y:double} point2
 * @return the middle point
 */
ORYX.Core.Math.midPoint = function(point1, point2) {
  return {
    x: (point1.x + point2.x) / 2.0,
    y: (point1.y + point2.y) / 2.0
  }
}

/**
 * Returns a TRUE if the point is over a line (defined by
 * point1 and point 2). In Addition a threshold can be set,
 * which defines the weight of those line.
 *
 * @param {int} pointX - Point X
 * @param {int} pointY - Point Y
 * @param {int} lPoint1X - Line first Point X
 * @param {int} lPoint1Y - Line first Point Y
 * @param {int} lPoint2X - Line second Point X
 * @param {int} lPoint2Y - Line second Point y
 * @param {int} offset {optional} - maximal distance to line
 * @class ORYX.Core.Math.prototype
 */
ORYX.Core.Math.isPointInLine = function(pointX, pointY, lPoint1X, lPoint1Y, lPoint2X, lPoint2Y, offset) {
  offset = offset ? Math.abs(offset) : 1

  // Check if the edge is vertical
  if (
    Math.abs(lPoint1X - lPoint2X) <= offset &&
    Math.abs(pointX - lPoint1X) <= offset &&
    pointY - Math.max(lPoint1Y, lPoint2Y) <= offset &&
    Math.min(lPoint1Y, lPoint2Y) - pointY <= offset
  ) {
    return true
  }

  // Check if the edge is horizontal
  if (
    Math.abs(lPoint1Y - lPoint2Y) <= offset &&
    Math.abs(pointY - lPoint1Y) <= offset &&
    pointX - Math.max(lPoint1X, lPoint2X) <= offset &&
    Math.min(lPoint1X, lPoint2X) - pointX <= offset
  ) {
    return true
  }

  if (pointX > Math.max(lPoint1X, lPoint2X) || pointX < Math.min(lPoint1X, lPoint2X)) {
    return false
  }

  if (pointY > Math.max(lPoint1Y, lPoint2Y) || pointY < Math.min(lPoint1Y, lPoint2Y)) {
    return false
  }

  var s = (lPoint1Y - lPoint2Y) / (lPoint1X - lPoint2X)

  return Math.abs(pointY - (s * pointX + lPoint1Y - s * lPoint1X)) < offset
}

/**
 * Get a boolean if the point is in the polygone
 *
 */
ORYX.Core.Math.isPointInEllipse = function(pointX, pointY, cx, cy, rx, ry) {
  if (cx === undefined || cy === undefined || rx === undefined || ry === undefined) {
    throw 'ORYX.Core.Math.isPointInEllipse needs a ellipse with these properties: x, y, radiusX, radiusY'
  }

  var tx = (pointX - cx) / rx
  var ty = (pointY - cy) / ry

  return tx * tx + ty * ty < 1.0
}

/**
 * Get a boolean if the point is in the polygone
 * @param {int} pointX
 * @param {int} pointY
 * @param {[int]} Cornerpoints of the Polygone (x,y,x,y,...)
 */
ORYX.Core.Math.isPointInPolygone = function(pointX, pointY, polygone) {
  if (arguments.length < 3) {
    throw 'ORYX.Core.Math.isPointInPolygone needs two arguments'
  }

  var lastIndex = polygone.length - 1

  if (polygone[0] !== polygone[lastIndex - 1] || polygone[1] !== polygone[lastIndex]) {
    polygone.push(polygone[0])
    polygone.push(polygone[1])
  }

  var crossings = 0

  var x1, y1, x2, y2, d

  for (var i = 0; i < polygone.length - 3; ) {
    x1 = polygone[i]
    y1 = polygone[++i]
    x2 = polygone[++i]
    y2 = polygone[i + 1]
    d = (pointY - y1) * (x2 - x1) - (pointX - x1) * (y2 - y1)

    if (y1 >= pointY != y2 >= pointY) {
      crossings += y2 - y1 >= 0 ? d >= 0 : d <= 0
    }
    if (
      !d &&
      Math.min(x1, x2) <= pointX &&
      pointX <= Math.max(x1, x2) &&
      Math.min(y1, y2) <= pointY &&
      pointY <= Math.max(y1, y2)
    ) {
      return true
    }
  }
  return crossings % 2 ? true : false
}

/**
 *	Calculates the distance between a point and a line. It is also testable, if
 *  the distance orthogonal to the line, matches the segment of the line.
 *
 *  @param {float} lineP1
 *  	The starting point of the line segment
 *  @param {float} lineP2
 *  	The end point of the line segment
 *  @param {Point} point
 *  	The point to calculate the distance to.
 *  @param {boolean} toSegmentOnly
 *  	Flag to signal if only the segment of the line shell be evaluated.
 */
ORYX.Core.Math.distancePointLinie = function(lineP1, lineP2, point, toSegmentOnly) {
  var intersectionPoint = ORYX.Core.Math.getPointOfIntersectionPointLine(lineP1, lineP2, point, toSegmentOnly)

  if (!intersectionPoint) {
    return null
  }

  return ORYX.Core.Math.getDistancePointToPoint(point, intersectionPoint)
}

/**
 * Calculates the distance between two points.
 *
 * @param {point} point1
 * @param {point} point2
 */
ORYX.Core.Math.getDistancePointToPoint = function(point1, point2) {
  return Math.sqrt(Math.pow(point1.x - point2.x, 2) + Math.pow(point1.y - point2.y, 2))
}

/**
 * Calculates the relative distance of a point which is between two other points.
 *
 * @param {point} between1
 * @param {point} between2
 * @param {point} point
 */
ORYX.Core.Math.getDistanceBetweenTwoPoints = function(between1, between2, point) {
  return (
    ORYX.Core.Math.getDistancePointToPoint(point, between1) / ORYX.Core.Math.getDistancePointToPoint(between1, between2)
  )
}

/**
 * Returns true, if the point is of the left hand
 * side of the regarding the line.
 *
 * @param {point} lineP1 Line first point
 * @param {point} lineP2 Line second point
 * @param {point} point
 */
ORYX.Core.Math.pointIsLeftOfLine = function(lineP1, lineP2, point) {
  var vec1 = ORYX.Core.Math.getVector(lineP1, lineP2)
  var vec2 = ORYX.Core.Math.getVector(lineP1, point)
  // if the cross produkt is more than 0
  return vec1.x * vec2.y - vec2.x * vec1.y > 0
}

/**
 * Calculates the a point which is relatively between two other points.
 *
 * @param {point} point1
 * @param {point} point2
 * @param {number} relative Relative which is between 0 and 1
 */
ORYX.Core.Math.getPointBetweenTwoPoints = function(point1, point2, relative) {
  relative = Math.max(Math.min(relative || 0, 1), 0)

  if (relative === 0) {
    return point1
  } else if (relative === 1) {
    return point2
  }

  return {
    x: point1.x + (point2.x - point1.x) * relative,
    y: point1.y + (point2.y - point1.y) * relative
  }
}

/**
 * Returns the vector of the both points
 *
 * @param {point} point1
 * @param {point} point2
 */
ORYX.Core.Math.getVector = function(point1, point2) {
  return {
    x: point2.x - point1.x,
    y: point2.y - point1.y
  }
}

/**
 * Returns the an identity vector of the given vector,
 * which has the length ot one.
 *
 * @param {point} vector
 * or
 * @param {point} point1
 * @param {point} point2
 */
ORYX.Core.Math.getIdentityVector = function(vector) {
  if (arguments.length == 2) {
    vector = ORYX.Core.Math.getVector(arguments[0], arguments[1])
  }

  var length = Math.sqrt(vector.x * vector.x + vector.y * vector.y)
  return {
    x: vector.x / (length || 1),
    y: vector.y / (length || 1)
  }
}

ORYX.Core.Math.getOrthogonalIdentityVector = function(point1, point2) {
  var vec = arguments.length == 1 ? point1 : ORYX.Core.Math.getIdentityVector(point1, point2)
  return {
    x: vec.y,
    y: -vec.x
  }
}

/**
 * Returns the intersection point of a line and a point that defines a line
 * orthogonal to the given line.
 *
 *  @param {float} lineP1
 *  	The starting point of the line segment
 *  @param {float} lineP2
 *  	The end point of the line segment
 *  @param {Point} point
 *  	The point to calculate the distance to.
 *  @param {boolean} onSegmentOnly
 *  	Flag to signal if only the segment of the line shell be evaluated.
 */
ORYX.Core.Math.getPointOfIntersectionPointLine = function(lineP1, lineP2, point, onSegmentOnly) {
  /*
   * [P3 - P1 - u(P2 - P1)] dot (P2 - P1) = 0
   * u =((x3-x1)(x2-x1)+(y3-y1)(y2-y1))/(p2-p1)²
   */
  var denominator = Math.pow(lineP2.x - lineP1.x, 2) + Math.pow(lineP2.y - lineP1.y, 2)
  if (denominator == 0) {
    return undefined
  }

  var u = ((point.x - lineP1.x) * (lineP2.x - lineP1.x) + (point.y - lineP1.y) * (lineP2.y - lineP1.y)) / denominator

  if (onSegmentOnly) {
    if (!(0 <= u && u <= 1)) {
      return undefined
    }
  }

  pointOfIntersection = new Object()
  pointOfIntersection.x = lineP1.x + u * (lineP2.x - lineP1.x)
  pointOfIntersection.y = lineP1.y + u * (lineP2.y - lineP1.y)

  return pointOfIntersection
}

/**
 * Translated the point with the given matrix.
 * @param {Point} point
 * @param {Matrix} matrix
 * @return {Object} Includes x, y
 */
ORYX.Core.Math.getTranslatedPoint = function(point, matrix) {
  var x = matrix.a * point.x + matrix.c * point.y + matrix.e * 1
  var y = matrix.b * point.x + matrix.d * point.y + matrix.f * 1
  return { x: x, y: y }
}

/**
 * Returns the inverse matrix of the given SVG transformation matrix
 * @param {SVGTransformationMatrix} matrix
 * @return {Matrix}
 */
ORYX.Core.Math.getInverseMatrix = function(matrix) {
  var det = ORYX.Core.Math.getDeterminant(matrix),
    m = matrix
  // +-     -+
  // | a c e |
  // | b d f |
  // | 0 0 1 |
  // +-     -+
  return {
    a: det * (m.d * 1 - m.f * 0),
    b: det * (m.f * 0 - m.b * 1),
    c: det * (m.e * 0 - m.c * 1),
    d: det * (m.a * 1 - m.e * 0),
    e: det * (m.c * m.f - m.e * m.d),
    f: det * (m.e * m.b - m.a * m.f)
  }
}

/**
 * Returns the determinant of the svg transformation matrix
 * @param {SVGTranformationMatrix} matrix
 * @return {Number}
 *
 */
ORYX.Core.Math.getDeterminant = function(m) {
  // a11a22a33+a12a23a31+a13a21a32-a13a22a31-a12a21a33-a11a23a32
  return m.a * m.d * 1 + m.c * m.f * 0 + m.e * m.b * 0 - m.e * m.d * 0 - m.c * m.b * 1 - m.a * m.f * 0
}

/**
 * Returns the bounding box of the given node. Translates the
 * origin bounding box with the tranlation matrix.
 * @param {SVGElement} node
 * @return {Object} Includes x, y, width, height
 */
ORYX.Core.Math.getTranslatedBoundingBox = function(node) {
  var matrix = node.getCTM()
  var bb = node.getBBox()
  var ul = ORYX.Core.Math.getTranslatedPoint({ x: bb.x, y: bb.y }, matrix)
  var ll = ORYX.Core.Math.getTranslatedPoint({ x: bb.x, y: bb.y + bb.height }, matrix)
  var ur = ORYX.Core.Math.getTranslatedPoint({ x: bb.x + bb.width, y: bb.y }, matrix)
  var lr = ORYX.Core.Math.getTranslatedPoint({ x: bb.x + bb.width, y: bb.y + bb.height }, matrix)

  var minPoint = {
    x: Math.min(ul.x, ll.x, ur.x, lr.x),
    y: Math.min(ul.y, ll.y, ur.y, lr.y)
  }
  var maxPoint = {
    x: Math.max(ul.x, ll.x, ur.x, lr.x),
    y: Math.max(ul.y, ll.y, ur.y, lr.y)
  }
  return {
    x: minPoint.x,
    y: minPoint.y,
    width: maxPoint.x - minPoint.x,
    height: maxPoint.y - minPoint.y
  }
}

/**
 * Returns the angle of the given line, which is representated by the two points
 * @param {Point} p1
 * @param {Point} p2
 * @return {Number} 0 <= x <= 359.99999
 */
ORYX.Core.Math.getAngle = function(p1, p2) {
  if (p1.x == p2.x && p1.y == p2.y) return 0

  var angle =
    (Math.asin(Math.sqrt(Math.pow(p1.y - p2.y, 2)) / Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p1.y - p2.y, 2))) *
      180) /
    Math.PI

  if (p2.x >= p1.x && p2.y <= p1.y) return angle
  else if (p2.x < p1.x && p2.y <= p1.y) return 180 - angle
  else if (p2.x < p1.x && p2.y > p1.y) return 180 + angle
  else return 360 - angle
}

/**
 * Implementation of the cohen-sutherland algorithm
 */
new (function() {
  var RIGHT = 2,
    TOP = 8,
    BOTTOM = 4,
    LEFT = 1

  function computeOutCode(x, y, xmin, ymin, xmax, ymax) {
    var code = 0
    if (y > ymax) code |= TOP
    else if (y < ymin) code |= BOTTOM
    if (x > xmax) code |= RIGHT
    else if (x < xmin) code |= LEFT
    return code
  }

  /**
   * Returns TRUE if the rectangle is over the edge and has intersection points or includes it
   * @param {Object} x1 Point A of the line
   * @param {Object} y1
   * @param {Object} x2 Point B of the line
   * @param {Object} y2
   * @param {Object} xmin Point A of the rectangle
   * @param {Object} ymin
   * @param {Object} xmax Point B of the rectangle
   * @param {Object} ymax
   */
  ORYX.Core.Math.isRectOverLine = function(x1, y1, x2, y2, xmin, ymin, xmax, ymax) {
    return !!ORYX.Core.Math.clipLineOnRect.apply(ORYX.Core.Math, arguments)
  }

  /**
   * Returns the clipped line on the given rectangle. If there is
   * no intersection, it will return NULL.
   *
   * @param {Object} x1 Point A of the line
   * @param {Object} y1
   * @param {Object} x2 Point B of the line
   * @param {Object} y2
   * @param {Object} xmin Point A of the rectangle
   * @param {Object} ymin
   * @param {Object} xmax Point B of the rectangle
   * @param {Object} ymax
   */
  ORYX.Core.Math.clipLineOnRect = function(x1, y1, x2, y2, xmin, ymin, xmax, ymax) {
    //Outcodes for P0, P1, and whatever point lies outside the clip rectangle
    var outcode0,
      outcode1,
      outcodeOut,
      hhh = 0
    var accept = false,
      done = false

    //compute outcodes
    outcode0 = computeOutCode(x1, y1, xmin, ymin, xmax, ymax)
    outcode1 = computeOutCode(x2, y2, xmin, ymin, xmax, ymax)

    do {
      if ((outcode0 | outcode1) == 0) {
        accept = true
        done = true
      } else if ((outcode0 & outcode1) > 0) {
        done = true
      } else {
        //failed both tests, so calculate the line segment to clip
        //from an outside point to an intersection with clip edge
        var x = 0,
          y = 0
        //At least one endpoint is outside the clip rectangle; pick it.
        outcodeOut = outcode0 != 0 ? outcode0 : outcode1
        //Now find the intersection point;
        //use formulas y = y0 + slope * (x - x0), x = x0 + (1/slope)* (y - y0)
        if ((outcodeOut & TOP) > 0) {
          x = x1 + ((x2 - x1) * (ymax - y1)) / (y2 - y1)
          y = ymax
        } else if ((outcodeOut & BOTTOM) > 0) {
          x = x1 + ((x2 - x1) * (ymin - y1)) / (y2 - y1)
          y = ymin
        } else if ((outcodeOut & RIGHT) > 0) {
          y = y1 + ((y2 - y1) * (xmax - x1)) / (x2 - x1)
          x = xmax
        } else if ((outcodeOut & LEFT) > 0) {
          y = y1 + ((y2 - y1) * (xmin - x1)) / (x2 - x1)
          x = xmin
        }

        //Now we move outside point to intersection point to clip
        //and get ready for next pass.
        if (outcodeOut == outcode0) {
          x1 = x
          y1 = y
          outcode0 = computeOutCode(x1, y1, xmin, ymin, xmax, ymax)
        } else {
          x2 = x
          y2 = y
          outcode1 = computeOutCode(x2, y2, xmin, ymin, xmax, ymax)
        }
      }
      hhh++
    } while (done != true && hhh < 5000)

    if (accept) {
      return { a: { x: x1, y: y1 }, b: { x: x2, y: y2 } }
    }
    return null
  }
})()

/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

/**
 * Init namespace
 */
if (!ORYX) {
  var ORYX = {}
}
if (!ORYX.Core) {
  ORYX.Core = {}
}
if (!ORYX.Core.StencilSet) {
  ORYX.Core.StencilSet = {}
}

/**
 * Class Stencil
 * uses Prototpye 1.5.0
 * uses Inheritance
 *
 * This class represents one stencil of a stencil set.
 */
ORYX.Core.StencilSet.Stencil = {
  /**
   * Constructor
   */
  construct: function(jsonStencil, namespace, source, stencilSet, propertyPackages, defaultPosition) {
    arguments.callee.$.construct.apply(this, arguments) // super();

    // check arguments and set defaults.
    if (!jsonStencil) throw 'Stencilset seems corrupt.'
    if (!namespace) throw 'Stencil does not provide namespace.'
    if (!source) throw 'Stencil does not provide SVG source.'
    if (!stencilSet) throw 'Fatal internal error loading stencilset.'
    //if(!propertyPackages) throw "Fatal internal error loading stencilset.";

    this._source = source
    this._jsonStencil = jsonStencil
    this._stencilSet = stencilSet
    this._namespace = namespace
    this._propertyPackages = propertyPackages

    if (defaultPosition && !this._jsonStencil.position) this._jsonStencil.position = defaultPosition

    this._view
    this._properties = new Hash()

    //init all JSON values
    if (!this._jsonStencil.type || !(this._jsonStencil.type === 'edge' || this._jsonStencil.type === 'node')) {
      throw 'ORYX.Core.StencilSet.Stencil(construct): Type is not defined.'
    }
    if (!this._jsonStencil.id || this._jsonStencil.id === '') {
      throw 'ORYX.Core.StencilSet.Stencil(construct): Id is not defined.'
    }
    if (!this._jsonStencil.title || this._jsonStencil.title === '') {
      throw 'ORYX.Core.StencilSet.Stencil(construct): Title is not defined.'
    }

    if (!this._jsonStencil.description) {
      this._jsonStencil.description = ''
    }
    if (!this._jsonStencil.groups) {
      this._jsonStencil.groups = []
    }
    if (!this._jsonStencil.roles) {
      this._jsonStencil.roles = []
    }

    //add id of stencil to its roles
    this._jsonStencil.roles.push(this._jsonStencil.id)

    //prepend namespace to each role
    this._jsonStencil.roles.each(
      function(role, index) {
        this._jsonStencil.roles[index] = namespace + role
      }.bind(this)
    )

    //delete duplicate roles
    this._jsonStencil.roles = this._jsonStencil.roles.uniq()

    //make id unique by prepending namespace of stencil set
    this._jsonStencil.id = namespace + this._jsonStencil.id

    this.postProcessProperties()

    // init serialize callback
    if (!this._jsonStencil.serialize) {
      this._jsonStencil.serialize = {}
      //this._jsonStencil.serialize = function(shape, data) { return data;};
    }

    // init deserialize callback
    if (!this._jsonStencil.deserialize) {
      this._jsonStencil.deserialize = {}
      //this._jsonStencil.deserialize = function(shape, data) { return data;};
    }

    // init layout callback
    if (!this._jsonStencil.layout) {
      this._jsonStencil.layout = []
      //this._jsonStencil.layout = function() {return true;}
    }

    //TODO does not work correctly, if the url does not exist
    //How to guarantee that the view is loaded correctly before leaving the constructor???
    var url = source + 'view/' + jsonStencil.view
    // override content type when this is webkit.

    /*
		if(Prototype.Browser.WebKit) {
			
			var req = new XMLHttpRequest;
			req.open("GET", url, false);
			req.overrideMimeType('text/xml');
			req.send(null);
			req.onload = (function() { _loadSVGOnSuccess(req.responseXML); }).bind(this);

		// else just do it.
		} else
		*/

    if (this._jsonStencil.view.trim().match(/</)) {
      var parser = new DOMParser()
      var xml = parser.parseFromString(this._jsonStencil.view, 'text/xml')

      //check if result is a SVG document
      if (ORYX.Editor.checkClassType(xml.documentElement, SVGSVGElement)) {
        this._view = xml.documentElement
      } else {
        throw 'ORYX.Core.StencilSet.Stencil(_loadSVGOnSuccess): The response is not a valid SVG document.'
      }
    } else {
      new Ajax.Request(url, {
        asynchronous: false,
        method: 'get',
        onSuccess: this._loadSVGOnSuccess.bind(this),
        onFailure: this._loadSVGOnFailure.bind(this)
      })
    }
  },

  postProcessProperties: function() {
    // add image path to icon
    this._jsonStencil.icon =
      ORYX.CONFIG.SERVER_HANDLER_ROOT + '/stencilsetitem/' + this._jsonStencil.objectId + '/image'

    // init property packages
    if (this._jsonStencil.propertyPackages && this._jsonStencil.propertyPackages instanceof Array) {
      var hiddenPropertyPackages = this._jsonStencil.hiddenPropertyPackages
      this._jsonStencil.propertyPackages.each(
        function(ppId) {
          var pp = this._propertyPackages.get(ppId)
          if (pp) {
            pp.each(
              function(prop) {
                var oProp = new ORYX.Core.StencilSet.Property(prop, this._namespace, this)
                
                var key = oProp.prefix() + '-' + oProp.id()
                // if (this._jsonStencil.title === 'BPMN.STENCILS.USERORDER.TITLE') {
                //   if (prop.id === 'name') {
                //     oProp._jsonProp.value = '用户发起'
                //   }

                //   if (prop.id === 'overrideid') {
                //     oProp._jsonProp.value = 'start_task_user_activity'
                //   }

                //   if (prop.id === 'usertaskassignment') {
                //     oProp._jsonProp.value = {"assignment":{"type":"idm","idm":{"type":"initiator"}}}
                //   }
                // }

                this._properties.set(key, oProp)

                // Check if we need to hide this property (ie it is there for display purposes,
                // if the user has filled it in, but it can no longer be edited)
                if (hiddenPropertyPackages.indexOf(oProp.id()) > -1) {
                  oProp.hide()
                }
              }.bind(this)
            )
          }
        }.bind(this)
      )
    }

    // init properties
    if (this._jsonStencil.properties && this._jsonStencil.properties instanceof Array) {
      this._jsonStencil.properties.each(
        function(prop) {
          var oProp = new ORYX.Core.StencilSet.Property(prop, this._namespace, this)
          var key = oProp.prefix() + '-' + oProp.id()
          this._properties.set(key, oProp)
        }.bind(this)
      )
    }
  },

  /**
   * @param {ORYX.Core.StencilSet.Stencil} stencil
   * @return {Boolean} True, if stencil has the same namespace and type.
   */
  equals: function(stencil) {
    return this.id() === stencil.id()
  },

  stencilSet: function() {
    return this._stencilSet
  },

  type: function() {
    return this._jsonStencil.type
  },

  namespace: function() {
    return this._namespace
  },

  id: function() {
    return this._jsonStencil.id
  },

  idWithoutNs: function() {
    return this.id().replace(this.namespace(), '')
  },

  title: function() {
    return ORYX.Core.StencilSet.getTranslation(this._jsonStencil, 'title')
  },

  description: function() {
    return ORYX.Core.StencilSet.getTranslation(this._jsonStencil, 'description')
  },

  groups: function() {
    return ORYX.Core.StencilSet.getTranslation(this._jsonStencil, 'groups')
  },

  position: function() {
    return isNaN(this._jsonStencil.position) ? 0 : this._jsonStencil.position
  },

  view: function() {
    return this._view.cloneNode(true) || this._view
  },

  icon: function() {
    return this._jsonStencil.icon
  },

  fixedAspectRatio: function() {
    return this._jsonStencil.fixedAspectRatio === true
  },

  hasMultipleRepositoryEntries: function() {
    return this.getRepositoryEntries().length > 0
  },

  getRepositoryEntries: function() {
    return this._jsonStencil.repositoryEntries ? $A(this._jsonStencil.repositoryEntries) : $A([])
  },

  properties: function() {
    return this._properties.values()
  },

  property: function(id) {
    return this._properties.get(id)
  },

  roles: function() {
    return this._jsonStencil.roles
  },

  defaultAlign: function() {
    if (!this._jsonStencil.defaultAlign) return 'east'
    return this._jsonStencil.defaultAlign
  },

  serialize: function(shape, data) {
    return this._jsonStencil.serialize
    //return this._jsonStencil.serialize(shape, data);
  },

  deserialize: function(shape, data) {
    return this._jsonStencil.deserialize
    //return this._jsonStencil.deserialize(shape, data);
  },

  // in which case is targetShape used?
  //	layout: function(shape, targetShape) {
  //		return this._jsonStencil.layout(shape, targetShape);
  //	},
  // layout property to store events for layouting in plugins
  layout: function(shape) {
    return this._jsonStencil.layout
  },

  addProperty: function(property, namespace) {
    if (property && namespace) {
      var oProp = new ORYX.Core.StencilSet.Property(property, namespace, this)
      this._properties[oProp.prefix() + '-' + oProp.id()] = oProp
    }
  },

  removeProperty: function(propertyId) {
    if (propertyId) {
      var oProp = this._properties.values().find(function(prop) {
        return propertyId == prop.id()
      })
      if (oProp) delete this._properties[oProp.prefix() + '-' + oProp.id()]
    }
  },

  _loadSVGOnSuccess: function(result) {
    var xml = null

    /*
     * We want to get a dom object for the requested file. Unfortunately,
     * safari has some issues here. this is meant as a fallback for all
     * browsers that don't recognize the svg mimetype as XML but support
     * data: urls on Ajax calls.
     */

    // responseXML != undefined.
    // if(!(result.responseXML))

    // get the dom by data: url.
    // xml = _evenMoreEvilHack(result.responseText, 'text/xml');

    // else

    // get it the usual way.
    xml = result.responseXML

    //check if result is a SVG document
    if (ORYX.Editor.checkClassType(xml.documentElement, SVGSVGElement)) {
      this._view = xml.documentElement
    } else {
      throw 'ORYX.Core.StencilSet.Stencil(_loadSVGOnSuccess): The response is not a SVG document.'
    }
  },

  _loadSVGOnFailure: function(result) {
    throw 'ORYX.Core.StencilSet.Stencil(_loadSVGOnFailure): Loading SVG document failed.'
  },

  toString: function() {
    return 'Stencil ' + this.title() + ' (' + this.id() + ')'
  }
}

ORYX.Core.StencilSet.Stencil = Clazz.extend(ORYX.Core.StencilSet.Stencil)

/**
 * Transform a string into an xml document, the Safari way, as long as
 * the nightlies are broken. Even more evil version.
 * @param {Object} str
 * @param {Object} contentType
 */
function _evenMoreEvilHack(str, contentType) {
  /*
   * This even more evil hack was taken from
   * http://web-graphics.com/mtarchive/001606.php#chatty004999
   */

  if (window.ActiveXObject) {
    var d = new ActiveXObject('MSXML.DomDocument')
    d.loadXML(str)
    return d
  } else if (window.XMLHttpRequest) {
    var req = new XMLHttpRequest()
    req.open('GET', 'data:' + (contentType || 'application/xml') + ';charset=utf-8,' + encodeURIComponent(str), false)
    if (req.overrideMimeType) {
      req.overrideMimeType(contentType)
    }
    req.send(null)
    return req.responseXML
  }
}

/**
 * Transform a string into an xml document, the Safari way, as long as
 * the nightlies are broken.
 * @param {Object} result the xml document object.
 */
function _evilSafariHack(serializedXML) {
  /*
   *  The Dave way. Taken from:
   *  http://web-graphics.com/mtarchive/001606.php
   *
   *  There is another possibility to parse XML in Safari, by implementing
   *  the DOMParser in javascript. However, in the latest nightlies of
   *  WebKit, DOMParser is already available, but still buggy. So, this is
   *  the best compromise for the time being.
   */

  var xml = serializedXML
  var url = 'data:text/xml;charset=utf-8,' + encodeURIComponent(xml)
  var dom = null

  // your standard AJAX stuff
  var req = new XMLHttpRequest()
  req.open('GET', url)
  req.onload = function() {
    dom = req.responseXML
  }
  req.send(null)

  return dom
}
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

/**
 * Init namespace
 */
if (!ORYX) {
  var ORYX = {}
}
if (!ORYX.Core) {
  ORYX.Core = {}
}
if (!ORYX.Core.StencilSet) {
  ORYX.Core.StencilSet = {}
}

/**
 * Class Property
 * uses Prototpye 1.5.0
 * uses Inheritance
 */
ORYX.Core.StencilSet.Property = Clazz.extend({
  /**
   * Constructor
   */
  construct: function(jsonProp, namespace, stencil) {
    arguments.callee.$.construct.apply(this, arguments)

    this._jsonProp = jsonProp || ORYX.Log.error('Parameter jsonProp is not defined.')
    this._namespace = namespace || ORYX.Log.error('Parameter namespace is not defined.')
    this._stencil = stencil || ORYX.Log.error('Parameter stencil is not defined.')

    this._items = {}
    this._complexItems = {}

    // Flag to indicate whether or not the property should be hidden
    // This can be for example when the stencil set is upgraded, but the model
    // has a value for that specific property filled in which we still want to show.
    // If the value is missing, the property can simply be not shown.
    this._hidden = false

    jsonProp.id = jsonProp.id || ORYX.Log.error('ORYX.Core.StencilSet.Property(construct): Id is not defined.')
    jsonProp.id = jsonProp.id.toLowerCase()

    if (!jsonProp.type) {
      ORYX.Log.info("Type is not defined for stencil '%0', id '%1'. Falling back to 'String'.", stencil, jsonProp.id)
      jsonProp.type = 'string'
    } else {
      jsonProp.type = jsonProp.type.toLowerCase()
    }

    jsonProp.prefix = jsonProp.prefix || 'oryx'
    jsonProp.title = jsonProp.title || ''
    jsonProp.value = jsonProp.value || ''
    jsonProp.description = jsonProp.description || ''
    jsonProp.readonly = jsonProp.readonly || false
    jsonProp.optional = jsonProp.optional !== false

    //init refToView
    if (this._jsonProp.refToView) {
      if (!(this._jsonProp.refToView instanceof Array)) {
        this._jsonProp.refToView = [this._jsonProp.refToView]
      }
    } else {
      this._jsonProp.refToView = []
    }

    var globalMin = this.getMinForType(jsonProp.type)
    if (jsonProp.min === undefined || jsonProp.min === null) {
      jsonProp.min = globalMin
    } else if (jsonProp.min < globalMin) {
      jsonProp.min = globalMin
    }

    var globalMax = this.getMaxForType(jsonProp.type)
    if (jsonProp.max === undefined || jsonProp.max === null) {
      jsonProp.max = globalMax
    } else if (jsonProp.max > globalMax) {
      jsonProp.min = globalMax
    }

    if (!jsonProp.fillOpacity) {
      jsonProp.fillOpacity = false
    }

    if ('number' != typeof jsonProp.lightness) {
      jsonProp.lightness = 1
    } else {
      jsonProp.lightness = Math.max(0, Math.min(1, jsonProp.lightness))
    }

    if (!jsonProp.strokeOpacity) {
      jsonProp.strokeOpacity = false
    }

    if (jsonProp.length === undefined || jsonProp.length === null) {
      jsonProp.length = Number.MAX_VALUE
    }

    if (!jsonProp.wrapLines) {
      jsonProp.wrapLines = false
    }

    if (!jsonProp.dateFormat) {
      jsonProp.dateFormat = ORYX.I18N.PropertyWindow.dateFormat || 'm/d/y'
    }

    if (!jsonProp.fill) {
      jsonProp.fill = false
    }

    if (!jsonProp.stroke) {
      jsonProp.stroke = false
    }

    if (!jsonProp.inverseBoolean) {
      jsonProp.inverseBoolean = false
    }

    if (!jsonProp.directlyEditable && jsonProp.directlyEditable != false) {
      jsonProp.directlyEditable = true
    }

    if (jsonProp.visible !== false) {
      jsonProp.visible = true
    }

    if (jsonProp.isList !== true) {
      jsonProp.isList = false

      if (!jsonProp.list || !(jsonProp.list instanceof Array)) {
        jsonProp.list = []
      }
    }

    if (!jsonProp.category) {
      if (jsonProp.popular) {
        jsonProp.category = 'popular'
      } else {
        jsonProp.category = 'others'
      }
    }

    if (!jsonProp.alwaysAppearInMultiselect) {
      jsonProp.alwaysAppearInMultiselect = false
    }

    if (jsonProp.type === ORYX.CONFIG.TYPE_CHOICE) {
      if (jsonProp.items && jsonProp.items instanceof Array) {
        jsonProp.items.each(
          function(jsonItem) {
            // why is the item's value used as the key???
            this._items[jsonItem.value.toLowerCase()] = new ORYX.Core.StencilSet.PropertyItem(jsonItem, namespace, this)
          }.bind(this)
        )
      } else {
        throw 'ORYX.Core.StencilSet.Property(construct): No property items defined.'
      }
      // extended by Kerstin (start)
    } else if (jsonProp.type === ORYX.CONFIG.TYPE_COMPLEX || jsonProp.type == ORYX.CONFIG.TYPE_MULTIPLECOMPLEX) {
      if (jsonProp.complexItems && jsonProp.complexItems instanceof Array) {
        jsonProp.complexItems.each(
          function(jsonComplexItem) {
            this._complexItems[jsonComplexItem.id.toLowerCase()] = new ORYX.Core.StencilSet.ComplexPropertyItem(
              jsonComplexItem,
              namespace,
              this
            )
          }.bind(this)
        )
      }
    }
    // extended by Kerstin (end)
  },

  getMinForType: function(type) {
    if (type.toLowerCase() == ORYX.CONFIG.TYPE_INTEGER) {
      return -Math.pow(2, 31)
    } else {
      return -Number.MAX_VALUE + 1
    }
  },
  getMaxForType: function(type) {
    if (type.toLowerCase() == ORYX.CONFIG.TYPE_INTEGER) {
      return Math.pow(2, 31) - 1
    } else {
      return Number.MAX_VALUE
    }
  },

  /**
   * @param {ORYX.Core.StencilSet.Property} property
   * @return {Boolean} True, if property has the same namespace and id.
   */
  equals: function(property) {
    return this._namespace === property.namespace() && this.id() === property.id() ? true : false
  },

  namespace: function() {
    return this._namespace
  },

  stencil: function() {
    return this._stencil
  },

  id: function() {
    return this._jsonProp.id
  },

  prefix: function() {
    return this._jsonProp.prefix
  },

  type: function() {
    return this._jsonProp.type
  },

  inverseBoolean: function() {
    return this._jsonProp.inverseBoolean
  },

  category: function() {
    return this._jsonProp.category
  },

  setCategory: function(value) {
    this._jsonProp.category = value
  },

  directlyEditable: function() {
    return this._jsonProp.directlyEditable
  },

  visible: function() {
    return this._jsonProp.visible
  },

  title: function() {
    return ORYX.Core.StencilSet.getTranslation(this._jsonProp, 'title')
  },

  value: function() {
    return this._jsonProp.value
  },

  readonly: function() {
    return this._jsonProp.readonly
  },

  optional: function() {
    return this._jsonProp.optional
  },

  description: function() {
    return ORYX.Core.StencilSet.getTranslation(this._jsonProp, 'description')
  },

  /**
   * An optional link to a SVG element so that the property affects the
   * graphical representation of the stencil.
   */
  refToView: function() {
    return this._jsonProp.refToView
  },

  /**
   * If type is integer or float, min is the lower bounds of value.
   */
  min: function() {
    return this._jsonProp.min
  },

  /**
   * If type ist integer or float, max is the upper bounds of value.
   */
  max: function() {
    return this._jsonProp.max
  },

  /**
   * If type is float, this method returns if the fill-opacity property should
   *  be set.
   *  @return {Boolean}
   */
  fillOpacity: function() {
    return this._jsonProp.fillOpacity
  },

  /**
   * If type is float, this method returns if the stroke-opacity property should
   *  be set.
   *  @return {Boolean}
   */
  strokeOpacity: function() {
    return this._jsonProp.strokeOpacity
  },

  /**
   * If type is string or richtext, length is the maximum length of the text.
   * TODO how long can a string be.
   */
  length: function() {
    return this._jsonProp.length ? this._jsonProp.length : Number.MAX_VALUE
  },

  wrapLines: function() {
    return this._jsonProp.wrapLines
  },

  /**
   * If type is date, dateFormat specifies the format of the date. The format
   * specification of the ext library is used:
   *
   * Format  Output      Description
   *	------  ----------  --------------------------------------------------------------
   *	  d      10         Day of the month, 2 digits with leading zeros
   *	  D      Wed        A textual representation of a day, three letters
   *	  j      10         Day of the month without leading zeros
   *	  l      Wednesday  A full textual representation of the day of the week
   *	  S      th         English ordinal day of month suffix, 2 chars (use with j)
   *	  w      3          Numeric representation of the day of the week
   *	  z      9          The julian date, or day of the year (0-365)
   *	  W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
   *	  F      January    A full textual representation of the month
   *	  m      01         Numeric representation of a month, with leading zeros
   *	  M      Jan        Month name abbreviation, three letters
   *	  n      1          Numeric representation of a month, without leading zeros
   *	  t      31         Number of days in the given month
   *	  L      0          Whether its a leap year (1 if it is a leap year, else 0)
   *	  Y      2007       A full numeric representation of a year, 4 digits
   *	  y      07         A two digit representation of a year
   *	  a      pm         Lowercase Ante meridiem and Post meridiem
   *	  A      PM         Uppercase Ante meridiem and Post meridiem
   *	  g      3          12-hour format of an hour without leading zeros
   *	  G      15         24-hour format of an hour without leading zeros
   *	  h      03         12-hour format of an hour with leading zeros
   *	  H      15         24-hour format of an hour with leading zeros
   *	  i      05         Minutes with leading zeros
   *	  s      01         Seconds, with leading zeros
   *	  O      -0600      Difference to Greenwich time (GMT) in hours
   *	  T      CST        Timezone setting of the machine running the code
   *	  Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
   *
   * Example:
   *  F j, Y, g:i a  ->  January 10, 2007, 3:05 pm
   */
  dateFormat: function() {
    return this._jsonProp.dateFormat
  },

  /**
   * If type is color, this method returns if the fill property should
   *  be set.
   *  @return {Boolean}
   */
  fill: function() {
    return this._jsonProp.fill
  },

  /**
   * Lightness defines the satiation of the color
   * 0 is the pure color
   * 1 is white
   * @return {Integer} lightness
   */
  lightness: function() {
    return this._jsonProp.lightness
  },

  /**
   * If type is color, this method returns if the stroke property should
   *  be set.
   *  @return {Boolean}
   */
  stroke: function() {
    return this._jsonProp.stroke
  },

  /**
   * If type is choice, items is a hash map with all alternative values
   * (PropertyItem objects) with id as keys.
   */
  items: function() {
    return $H(this._items).values()
  },

  item: function(value) {
    if (value) {
      return this._items[value.toLowerCase()]
    } else {
      return null
    }
  },

  toString: function() {
    return 'Property ' + this.title() + ' (' + this.id() + ')'
  },

  complexItems: function() {
    return $H(this._complexItems).values()
  },

  complexItem: function(id) {
    if (id) {
      return this._complexItems[id.toLowerCase()]
    } else {
      return null
    }
  },

  complexAttributeToView: function() {
    return this._jsonProp.complexAttributeToView || ''
  },

  isList: function() {
    return !!this._jsonProp.isList
  },

  getListItems: function() {
    return this._jsonProp.list
  },

  /**
   * If type is glossary link, the
   * type of category can be defined where
   * the link only can go to.
   * @return {String} The glossary category id
   */
  linkableType: function() {
    return this._jsonProp.linkableType || ''
  },

  alwaysAppearInMultiselect: function() {
    return this._jsonProp.alwaysAppearInMultiselect
  },

  popular: function() {
    return this._jsonProp.popular || false
  },

  setPopular: function() {
    this._jsonProp.popular = true
  },

  hide: function() {
    this._hidden = true
  },

  isHidden: function() {
    return this._hidden
  }
})
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

/**
 * Init namespace
 */
if (!ORYX) {
  var ORYX = {}
}
if (!ORYX.Core) {
  ORYX.Core = {}
}
if (!ORYX.Core.StencilSet) {
  ORYX.Core.StencilSet = {}
}

/**
 * Class Stencil
 * uses Prototpye 1.5.0
 * uses Inheritance
 */
ORYX.Core.StencilSet.PropertyItem = Clazz.extend({
  /**
   * Constructor
   */
  construct: function(jsonItem, namespace, property) {
    arguments.callee.$.construct.apply(this, arguments)

    if (!jsonItem) {
      throw 'ORYX.Core.StencilSet.PropertyItem(construct): Parameter jsonItem is not defined.'
    }
    if (!namespace) {
      throw 'ORYX.Core.StencilSet.PropertyItem(construct): Parameter namespace is not defined.'
    }
    if (!property) {
      throw 'ORYX.Core.StencilSet.PropertyItem(construct): Parameter property is not defined.'
    }

    this._jsonItem = jsonItem
    this._namespace = namespace
    this._property = property

    //init all values
    if (!jsonItem.value) {
      throw 'ORYX.Core.StencilSet.PropertyItem(construct): Value is not defined.'
    }

    if (this._jsonItem.refToView) {
      if (!(this._jsonItem.refToView instanceof Array)) {
        this._jsonItem.refToView = [this._jsonItem.refToView]
      }
    } else {
      this._jsonItem.refToView = []
    }
  },

  /**
   * @param {ORYX.Core.StencilSet.PropertyItem} item
   * @return {Boolean} True, if item has the same namespace and id.
   */
  equals: function(item) {
    return this.property().equals(item.property()) && this.value() === item.value()
  },

  namespace: function() {
    return this._namespace
  },

  property: function() {
    return this._property
  },

  value: function() {
    return this._jsonItem.value
  },

  title: function() {
    return ORYX.Core.StencilSet.getTranslation(this._jsonItem, 'title')
  },

  refToView: function() {
    return this._jsonItem.refToView
  },

  icon: function() {
    return this._jsonItem.icon ? this.property().stencil()._source + 'icons/' + this._jsonItem.icon : ''
  },

  toString: function() {
    return 'PropertyItem ' + this.property() + ' (' + this.value() + ')'
  }
})
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

/**
 * Init namespaces
 */
if (!ORYX) {
  var ORYX = {}
}
if (!ORYX.Core) {
  ORYX.Core = {}
}
if (!ORYX.Core.StencilSet) {
  ORYX.Core.StencilSet = {}
}

/**
 * Class Stencil
 * uses Prototpye 1.5.0
 * uses Inheritance
 */
ORYX.Core.StencilSet.ComplexPropertyItem = Clazz.extend({
  /**
   * Constructor
   */
  construct: function(jsonItem, namespace, property) {
    arguments.callee.$.construct.apply(this, arguments)

    if (!jsonItem) {
      throw 'ORYX.Core.StencilSet.ComplexPropertyItem(construct): Parameter jsonItem is not defined.'
    }
    if (!namespace) {
      throw 'ORYX.Core.StencilSet.ComplexPropertyItem(construct): Parameter namespace is not defined.'
    }
    if (!property) {
      throw 'ORYX.Core.StencilSet.ComplexPropertyItem(construct): Parameter property is not defined.'
    }

    this._jsonItem = jsonItem
    this._namespace = namespace
    this._property = property
    this._items = new Hash()
    this._complexItems = new Hash()

    //init all values
    if (!jsonItem.name) {
      throw 'ORYX.Core.StencilSet.ComplexPropertyItem(construct): Name is not defined.'
    }

    if (!jsonItem.type) {
      throw 'ORYX.Core.StencilSet.ComplexPropertyItem(construct): Type is not defined.'
    } else {
      jsonItem.type = jsonItem.type.toLowerCase()
    }

    if (jsonItem.type === ORYX.CONFIG.TYPE_CHOICE) {
      if (jsonItem.items && jsonItem.items instanceof Array) {
        jsonItem.items.each(
          function(item) {
            this._items[item.value] = new ORYX.Core.StencilSet.PropertyItem(item, namespace, this)
          }.bind(this)
        )
      } else {
        throw 'ORYX.Core.StencilSet.Property(construct): No property items defined.'
      }
    } else if (jsonItem.type === ORYX.CONFIG.TYPE_COMPLEX) {
      if (jsonItem.complexItems && jsonItem.complexItems instanceof Array) {
        jsonItem.complexItems.each(
          function(complexItem) {
            this._complexItems[complexItem.id] = new ORYX.Core.StencilSet.ComplexPropertyItem(
              complexItem,
              namespace,
              this
            )
          }.bind(this)
        )
      } else {
        throw 'ORYX.Core.StencilSet.Property(construct): No property items defined.'
      }
    }
  },

  /**
   * @param {ORYX.Core.StencilSet.PropertyItem} item
   * @return {Boolean} True, if item has the same namespace and id.
   */
  equals: function(item) {
    return this.property().equals(item.property()) && this.name() === item.name()
  },

  namespace: function() {
    return this._namespace
  },

  property: function() {
    return this._property
  },

  name: function() {
    return ORYX.Core.StencilSet.getTranslation(this._jsonItem, 'name')
  },

  id: function() {
    return this._jsonItem.id
  },

  type: function() {
    return this._jsonItem.type
  },

  optional: function() {
    return this._jsonItem.optional
  },

  width: function() {
    return this._jsonItem.width
  },

  value: function() {
    return this._jsonItem.value
  },

  items: function() {
    return this._items.values()
  },

  complexItems: function() {
    return this._complexItems.values()
  },

  disable: function() {
    return this._jsonItem.disable
  }
})
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

/**
 * Init namespaces
 */
if (!ORYX) {
  var ORYX = {}
}
if (!ORYX.Core) {
  ORYX.Core = {}
}
if (!ORYX.Core.StencilSet) {
  ORYX.Core.StencilSet = {}
}

/**
 * Class Rules uses Prototpye 1.5.0 uses Inheritance
 *
 * This class implements the API to check the stencil sets' rules.
 */
ORYX.Core.StencilSet.Rules = {
  /**
   * Constructor
   */
  construct: function() {
    arguments.callee.$.construct.apply(this, arguments)

    this._stencilSets = []
    this._stencils = []
    this._containerStencils = []

    this._cachedConnectSET = new Hash()
    this._cachedConnectSE = new Hash()
    this._cachedConnectTE = new Hash()
    this._cachedCardSE = new Hash()
    this._cachedCardTE = new Hash()
    this._cachedContainPC = new Hash()
    this._cachedMorphRS = new Hash()

    this._connectionRules = new Hash()
    this._cardinalityRules = new Hash()
    this._containmentRules = new Hash()
    this._morphingRules = new Hash()
    this._layoutRules = new Hash()
  },

  /**
   * Call this method to initialize the rules for a stencil set and all of its
   * active extensions.
   *
   * @param {Object}
   *            stencilSet
   */
  initializeRules: function(stencilSet) {
    var existingSS = this._stencilSets.find(function(ss) {
      return ss.namespace() == stencilSet.namespace()
    })
    if (existingSS) {
      // reinitialize all rules
      var stencilsets = this._stencilSets.clone()
      stencilsets = stencilsets.without(existingSS)
      stencilsets.push(stencilSet)

      this._stencilSets = []
      this._stencils = []
      this._containerStencils = []

      this._cachedConnectSET = new Hash()
      this._cachedConnectSE = new Hash()
      this._cachedConnectTE = new Hash()
      this._cachedCardSE = new Hash()
      this._cachedCardTE = new Hash()
      this._cachedContainPC = new Hash()
      this._cachedMorphRS = new Hash()

      this._connectionRules = new Hash()
      this._cardinalityRules = new Hash()
      this._containmentRules = new Hash()
      this._morphingRules = new Hash()
      this._layoutRules = new Hash()

      stencilsets.each(
        function(ss) {
          this.initializeRules(ss)
        }.bind(this)
      )
      return
    } else {
      this._stencilSets.push(stencilSet)

      var jsonRules = new Hash(stencilSet.jsonRules())
      var namespace = stencilSet.namespace()
      var stencils = stencilSet.stencils()

      stencilSet
        .extensions()
        .values()
        .each(function(extension) {
          if (extension.rules) {
            if (extension.rules.connectionRules)
              jsonRules.connectionRules = jsonRules.connectionRules.concat(extension.rules.connectionRules)
            if (extension.rules.cardinalityRules)
              jsonRules.cardinalityRules = jsonRules.cardinalityRules.concat(extension.rules.cardinalityRules)
            if (extension.rules.containmentRules)
              jsonRules.containmentRules = jsonRules.containmentRules.concat(extension.rules.containmentRules)
            if (extension.rules.morphingRules)
              jsonRules.morphingRules = jsonRules.morphingRules.concat(extension.rules.morphingRules)
          }
          if (extension.stencils) stencils = stencils.concat(extension.stencils)
        })

      this._stencils = this._stencils.concat(stencilSet.stencils())

      // init connection rules
      var cr = this._connectionRules
      if (jsonRules.get('connectionRules')) {
        jsonRules.get('connectionRules').each(
          function(rules) {
            if (this._isRoleOfOtherNamespace(rules.role)) {
              if (!cr.get(rules.role)) {
                cr.set(rules.role, new Hash())
              }
            } else {
              if (!cr.get(namespace + rules.role)) cr.set(namespace + rules.role, new Hash())
            }

            rules.connects.each(
              function(connect) {
                var toRoles = []
                if (connect.to) {
                  if (!(connect.to instanceof Array)) {
                    connect.to = [connect.to]
                  }
                  connect.to.each(
                    function(to) {
                      if (this._isRoleOfOtherNamespace(to)) {
                        toRoles.push(to)
                      } else {
                        toRoles.push(namespace + to)
                      }
                    }.bind(this)
                  )
                }

                var role, from
                if (this._isRoleOfOtherNamespace(rules.role)) role = rules.role
                else role = namespace + rules.role

                if (this._isRoleOfOtherNamespace(connect.from)) from = connect.from
                else from = namespace + connect.from

                if (!cr.get(role).get(from)) cr.get(role).set(from, toRoles)
                else
                  cr.get(role).set(
                    from,
                    cr
                      .get(role)
                      .get(from)
                      .concat(toRoles)
                  )
              }.bind(this)
            )
          }.bind(this)
        )
      }

      // init cardinality rules
      var cardr = this._cardinalityRules
      if (jsonRules.get('cardinalityRules')) {
        jsonRules.get('cardinalityRules').each(
          function(rules) {
            var cardrKey
            if (this._isRoleOfOtherNamespace(rules.role)) {
              cardrKey = rules.role
            } else {
              cardrKey = namespace + rules.role
            }

            if (!cardr.get(cardrKey)) {
              cardr.set(cardrKey, {})
              for (i in rules) {
                cardr.get(cardrKey)[i] = rules[i]
              }
            }

            var oe = new Hash()
            if (rules.outgoingEdges) {
              rules.outgoingEdges.each(
                function(rule) {
                  if (this._isRoleOfOtherNamespace(rule.role)) {
                    oe.set(rule.role, rule)
                  } else {
                    oe.set(namespace + rule.role, rule)
                  }
                }.bind(this)
              )
            }
            cardr.get(cardrKey).outgoingEdges = oe
            var ie = new Hash()
            if (rules.incomingEdges) {
              rules.incomingEdges.each(
                function(rule) {
                  if (this._isRoleOfOtherNamespace(rule.role)) {
                    ie.set(rule.role, rule)
                  } else {
                    ie.set(namespace + rule.role, rule)
                  }
                }.bind(this)
              )
            }
            cardr.get(cardrKey).incomingEdges = ie
          }.bind(this)
        )
      }

      // init containment rules
      var conr = this._containmentRules
      if (jsonRules.get('containmentRules')) {
        jsonRules.get('containmentRules').each(
          function(rules) {
            var conrKey
            if (this._isRoleOfOtherNamespace(rules.role)) {
              conrKey = rules.role
            } else {
              this._containerStencils.push(namespace + rules.role)
              conrKey = namespace + rules.role
            }
            if (!conr.get(conrKey)) {
              conr.set(conrKey, [])
            }
            ;(rules.contains || []).each(
              function(containRole) {
                if (this._isRoleOfOtherNamespace(containRole)) {
                  conr.get(conrKey).push(containRole)
                } else {
                  conr.get(conrKey).push(namespace + containRole)
                }
              }.bind(this)
            )
          }.bind(this)
        )
      }

      // init morphing rules
      var morphr = this._morphingRules
      if (jsonRules.get('morphingRules')) {
        jsonRules.get('morphingRules').each(
          function(rules) {
            var morphrKey
            if (this._isRoleOfOtherNamespace(rules.role)) {
              morphrKey = rules.role
            } else {
              morphrKey = namespace + rules.role
            }
            if (!morphr.get(morphrKey)) {
              morphr.set(morphrKey, [])
            }
            if (!rules.preserveBounds) {
              rules.preserveBounds = false
            }
            rules.baseMorphs.each(
              function(baseMorphStencilId) {
                var morphStencil = this._getStencilById(namespace + baseMorphStencilId)
                if (morphStencil) {
                  morphr.get(morphrKey).push(morphStencil)
                }
              }.bind(this)
            )
          }.bind(this)
        )
      }

      // init layouting rules
      var layoutRules = this._layoutRules
      if (jsonRules.get('layoutRules')) {
        var getDirections = function(o) {
          return {
            edgeRole: o.edgeRole || undefined,
            t: o['t'] || 1,
            r: o['r'] || 1,
            b: o['b'] || 1,
            l: o['l'] || 1
          }
        }

        jsonRules.get('layoutRules').each(
          function(rules) {
            var layoutKey
            if (this._isRoleOfOtherNamespace(rules.role)) {
              layoutKey = rules.role
            } else {
              layoutKey = namespace + rules.role
            }
            if (!layoutRules.get(layoutKey)) {
              layoutRules.set(layoutKey, {})
            }
            if (rules['in']) {
              layoutRules.get(layoutKey)['in'] = getDirections(rules['in'])
            }
            if (rules['ins']) {
              layoutRules.get(layoutKey)['ins'] = (rules['ins'] || []).map(function(e) {
                return getDirections(e)
              })
            }
            if (rules['out']) {
              layoutRules.get(layoutKey)['out'] = getDirections(rules['out'])
            }
            if (rules['outs']) {
              layoutRules.get(layoutKey)['outs'] = (rules['outs'] || []).map(function(e) {
                return getDirections(e)
              })
            }
          }.bind(this)
        )
      }
    }
  },

  _getStencilById: function(id) {
    return this._stencils.find(function(stencil) {
      return stencil.id() == id
    })
  },

  _cacheConnect: function(args) {
    result = this._canConnect(args)

    if (args.sourceStencil && args.targetStencil) {
      var source = this._cachedConnectSET[args.sourceStencil.id()]

      if (!source) {
        source = new Hash()
        this._cachedConnectSET[args.sourceStencil.id()] = source
      }

      var edge = source[args.edgeStencil.id()]

      if (!edge) {
        edge = new Hash()
        source[args.edgeStencil.id()] = edge
      }

      edge[args.targetStencil.id()] = result
    } else if (args.sourceStencil) {
      var source = this._cachedConnectSE[args.sourceStencil.id()]

      if (!source) {
        source = new Hash()
        this._cachedConnectSE[args.sourceStencil.id()] = source
      }

      source[args.edgeStencil.id()] = result
    } else {
      var target = this._cachedConnectTE[args.targetStencil.id()]

      if (!target) {
        target = new Hash()
        this._cachedConnectTE[args.targetStencil.id()] = target
      }

      target[args.edgeStencil.id()] = result
    }

    return result
  },

  _cacheCard: function(args) {
    if (args.sourceStencil) {
      var source = this._cachedCardSE[args.sourceStencil.id()]

      if (!source) {
        source = new Hash()
        this._cachedCardSE[args.sourceStencil.id()] = source
      }

      var max = this._getMaximumNumberOfOutgoingEdge(args)
      if (max == undefined) max = -1

      source[args.edgeStencil.id()] = max
    }

    if (args.targetStencil) {
      var target = this._cachedCardTE[args.targetStencil.id()]

      if (!target) {
        target = new Hash()
        this._cachedCardTE[args.targetStencil.id()] = target
      }

      var max = this._getMaximumNumberOfIncomingEdge(args)
      if (max == undefined) max = -1

      target[args.edgeStencil.id()] = max
    }
  },

  _cacheContain: function(args) {
    var result = [this._canContain(args), this._getMaximumOccurrence(args.containingStencil, args.containedStencil)]

    if (result[1] == undefined) result[1] = -1

    var children = this._cachedContainPC[args.containingStencil.id()]

    if (!children) {
      children = new Hash()
      this._cachedContainPC[args.containingStencil.id()] = children
    }

    children[args.containedStencil.id()] = result

    return result
  },

  /**
   * Returns all stencils belonging to a morph group. (calculation result is
   * cached)
   */
  _cacheMorph: function(role) {
    var morphs = this._cachedMorphRS[role]

    if (!morphs) {
      morphs = []

      if (this._morphingRules.keys().include(role)) {
        morphs = this._stencils.select(function(stencil) {
          return stencil.roles().include(role)
        })
      }

      this._cachedMorphRS[role] = morphs
    }
    return morphs
  },

  /** Begin connection rules' methods */

  /**
   *
   * @param {Object}
   *            args sourceStencil: ORYX.Core.StencilSet.Stencil | undefined
   *            sourceShape: ORYX.Core.Shape | undefined
   *
   * At least sourceStencil or sourceShape has to be specified
   *
   * @return {Array} Array of stencils of edges that can be outgoing edges of
   *         the source.
   */
  outgoingEdgeStencils: function(args) {
    // check arguments
    if (!args.sourceShape && !args.sourceStencil) {
      return []
    }

    // init arguments
    if (args.sourceShape) {
      args.sourceStencil = args.sourceShape.getStencil()
    }

    var _edges = []

    // test each edge, if it can connect to source
    this._stencils.each(
      function(stencil) {
        if (stencil.type() === 'edge') {
          var newArgs = Object.clone(args)
          newArgs.edgeStencil = stencil
          if (this.canConnect(newArgs)) {
            _edges.push(stencil)
          }
        }
      }.bind(this)
    )

    return _edges
  },

  /**
   *
   * @param {Object}
   *            args targetStencil: ORYX.Core.StencilSet.Stencil | undefined
   *            targetShape: ORYX.Core.Shape | undefined
   *
   * At least targetStencil or targetShape has to be specified
   *
   * @return {Array} Array of stencils of edges that can be incoming edges of
   *         the target.
   */
  incomingEdgeStencils: function(args) {
    // check arguments
    if (!args.targetShape && !args.targetStencil) {
      return []
    }

    // init arguments
    if (args.targetShape) {
      args.targetStencil = args.targetShape.getStencil()
    }

    var _edges = []

    // test each edge, if it can connect to source
    this._stencils.each(
      function(stencil) {
        if (stencil.type() === 'edge') {
          var newArgs = Object.clone(args)
          newArgs.edgeStencil = stencil
          if (this.canConnect(newArgs)) {
            _edges.push(stencil)
          }
        }
      }.bind(this)
    )

    return _edges
  },

  /**
   *
   * @param {Object}
   *            args edgeStencil: ORYX.Core.StencilSet.Stencil | undefined
   *            edgeShape: ORYX.Core.Edge | undefined targetStencil:
   *            ORYX.Core.StencilSet.Stencil | undefined targetShape:
   *            ORYX.Core.Node | undefined
   *
   * At least edgeStencil or edgeShape has to be specified!!!
   *
   * @return {Array} Returns an array of stencils that can be source of the
   *         specified edge.
   */
  sourceStencils: function(args) {
    // check arguments
    if (!args || (!args.edgeShape && !args.edgeStencil)) {
      return []
    }

    // init arguments
    if (args.targetShape) {
      args.targetStencil = args.targetShape.getStencil()
    }

    if (args.edgeShape) {
      args.edgeStencil = args.edgeShape.getStencil()
    }

    var _sources = []

    // check each stencil, if it can be a source
    this._stencils.each(
      function(stencil) {
        var newArgs = Object.clone(args)
        newArgs.sourceStencil = stencil
        if (this.canConnect(newArgs)) {
          _sources.push(stencil)
        }
      }.bind(this)
    )

    return _sources
  },

  /**
   *
   * @param {Object}
   *            args edgeStencil: ORYX.Core.StencilSet.Stencil | undefined
   *            edgeShape: ORYX.Core.Edge | undefined sourceStencil:
   *            ORYX.Core.StencilSet.Stencil | undefined sourceShape:
   *            ORYX.Core.Node | undefined
   *
   * At least edgeStencil or edgeShape has to be specified!!!
   *
   * @return {Array} Returns an array of stencils that can be target of the
   *         specified edge.
   */
  targetStencils: function(args) {
    // check arguments
    if (!args || (!args.edgeShape && !args.edgeStencil)) {
      return []
    }

    // init arguments
    if (args.sourceShape) {
      args.sourceStencil = args.sourceShape.getStencil()
    }

    if (args.edgeShape) {
      args.edgeStencil = args.edgeShape.getStencil()
    }

    var _targets = []

    // check stencil, if it can be a target
    this._stencils.each(
      function(stencil) {
        var newArgs = Object.clone(args)
        newArgs.targetStencil = stencil
        if (this.canConnect(newArgs)) {
          _targets.push(stencil)
        }
      }.bind(this)
    )

    return _targets
  },

  /**
   *
   * @param {Object}
   *            args edgeStencil: ORYX.Core.StencilSet.Stencil edgeShape:
   *            ORYX.Core.Edge |undefined sourceStencil:
   *            ORYX.Core.StencilSet.Stencil | undefined sourceShape:
   *            ORYX.Core.Node |undefined targetStencil:
   *            ORYX.Core.StencilSet.Stencil | undefined targetShape:
   *            ORYX.Core.Node |undefined
   *
   * At least source or target has to be specified!!!
   *
   * @return {Boolean} Returns, if the edge can connect source and target.
   */
  canConnect: function(args) {
    // check arguments
    if (
      !args ||
      (!args.sourceShape && !args.sourceStencil && !args.targetShape && !args.targetStencil) ||
      (!args.edgeShape && !args.edgeStencil)
    ) {
      return false
    }

    // init arguments
    if (args.sourceShape) {
      args.sourceStencil = args.sourceShape.getStencil()
    }
    if (args.targetShape) {
      args.targetStencil = args.targetShape.getStencil()
    }
    if (args.edgeShape) {
      args.edgeStencil = args.edgeShape.getStencil()
    }

    var result

    if (args.sourceStencil && args.targetStencil) {
      var source = this._cachedConnectSET[args.sourceStencil.id()]

      if (!source) result = this._cacheConnect(args)
      else {
        var edge = source[args.edgeStencil.id()]

        if (!edge) result = this._cacheConnect(args)
        else {
          var target = edge[args.targetStencil.id()]

          if (target == undefined) result = this._cacheConnect(args)
          else result = target
        }
      }
    } else if (args.sourceStencil) {
      var source = this._cachedConnectSE[args.sourceStencil.id()]

      if (!source) result = this._cacheConnect(args)
      else {
        var edge = source[args.edgeStencil.id()]

        if (edge == undefined) result = this._cacheConnect(args)
        else result = edge
      }
    } else {
      // args.targetStencil
      var target = this._cachedConnectTE[args.targetStencil.id()]

      if (!target) result = this._cacheConnect(args)
      else {
        var edge = target[args.edgeStencil.id()]

        if (edge == undefined) result = this._cacheConnect(args)
        else result = edge
      }
    }

    // check cardinality
    if (result) {
      if (args.sourceShape) {
        var source = this._cachedCardSE[args.sourceStencil.id()]

        if (!source) {
          this._cacheCard(args)
          source = this._cachedCardSE[args.sourceStencil.id()]
        }

        var max = source[args.edgeStencil.id()]

        if (max == undefined) {
          this._cacheCard(args)
        }

        max = source[args.edgeStencil.id()]

        if (max != -1) {
          result = args.sourceShape.getOutgoingShapes().all(function(cs) {
            if (cs.getStencil().id() === args.edgeStencil.id() && (args.edgeShape ? cs !== args.edgeShape : true)) {
              max--
              return max > 0 ? true : false
            } else {
              return true
            }
          })
        }
      }

      if (args.targetShape) {
        var target = this._cachedCardTE[args.targetStencil.id()]

        if (!target) {
          this._cacheCard(args)
          target = this._cachedCardTE[args.targetStencil.id()]
        }

        var max = target[args.edgeStencil.id()]

        if (max == undefined) {
          this._cacheCard(args)
        }

        max = target[args.edgeStencil.id()]

        if (max != -1) {
          result = args.targetShape.getIncomingShapes().all(function(cs) {
            if (cs.getStencil().id() === args.edgeStencil.id() && (args.edgeShape ? cs !== args.edgeShape : true)) {
              max--
              return max > 0 ? true : false
            } else {
              return true
            }
          })
        }
      }
    }

    return result
  },

  /**
   *
   * @param {Object}
   *            args edgeStencil: ORYX.Core.StencilSet.Stencil edgeShape:
   *            ORYX.Core.Edge |undefined sourceStencil:
   *            ORYX.Core.StencilSet.Stencil | undefined sourceShape:
   *            ORYX.Core.Node |undefined targetStencil:
   *            ORYX.Core.StencilSet.Stencil | undefined targetShape:
   *            ORYX.Core.Node |undefined
   *
   * At least source or target has to be specified!!!
   *
   * @return {Boolean} Returns, if the edge can connect source and target.
   */
  _canConnect: function(args) {
    // check arguments
    if (
      !args ||
      (!args.sourceShape && !args.sourceStencil && !args.targetShape && !args.targetStencil) ||
      (!args.edgeShape && !args.edgeStencil)
    ) {
      return false
    }

    // init arguments
    if (args.sourceShape) {
      args.sourceStencil = args.sourceShape.getStencil()
    }
    if (args.targetShape) {
      args.targetStencil = args.targetShape.getStencil()
    }
    if (args.edgeShape) {
      args.edgeStencil = args.edgeShape.getStencil()
    }

    // 1. check connection rules
    var resultCR

    // get all connection rules for this edge
    var edgeRules = this._getConnectionRulesOfEdgeStencil(args.edgeStencil)

    // check connection rules, if the source can be connected to the target
    // with the specified edge.
    if (edgeRules.keys().length === 0) {
      resultCR = false
    } else {
      if (args.sourceStencil) {
        resultCR = args.sourceStencil.roles().any(function(sourceRole) {
          var targetRoles = edgeRules.get(sourceRole)

          if (!targetRoles) {
            return false
          }

          if (args.targetStencil) {
            return targetRoles.any(function(targetRole) {
              return args.targetStencil.roles().member(targetRole)
            })
          } else {
            return true
          }
        })
      } else {
        // !args.sourceStencil -> there is args.targetStencil
        resultCR = edgeRules.values().any(function(targetRoles) {
          return args.targetStencil.roles().any(function(targetRole) {
            return targetRoles.member(targetRole)
          })
        })
      }
    }

    return resultCR
  },

  /** End connection rules' methods */

  /** Begin containment rules' methods */

  isContainer: function(shape) {
    return this._containerStencils.member(shape.getStencil().id())
  },

  /**
   *
   * @param {Object}
   *            args containingStencil: ORYX.Core.StencilSet.Stencil
   *            containingShape: ORYX.Core.AbstractShape containedStencil:
   *            ORYX.Core.StencilSet.Stencil containedShape: ORYX.Core.Shape
   */
  canContain: function(args) {
    if (
      !args ||
      (!args.containingStencil && !args.containingShape) ||
      (!args.containedStencil && !args.containedShape)
    ) {
      return false
    }

    // init arguments
    if (args.containedShape) {
      args.containedStencil = args.containedShape.getStencil()
    }

    if (args.containingShape) {
      args.containingStencil = args.containingShape.getStencil()
    }

    //if(args.containingStencil.type() == 'edge' || args.containedStencil.type() == 'edge')
    //	return false;
    if (args.containedStencil.type() == 'edge') return false

    var childValues

    var parent = this._cachedContainPC[args.containingStencil.id()]

    if (!parent) childValues = this._cacheContain(args)
    else {
      childValues = parent[args.containedStencil.id()]

      if (!childValues) childValues = this._cacheContain(args)
    }

    if (!childValues[0]) return false
    else if (childValues[1] == -1) return true
    else {
      if (args.containingShape) {
        var max = childValues[1]
        return args.containingShape.getChildShapes(false).all(function(as) {
          if (as.getStencil().id() === args.containedStencil.id()) {
            max--
            return max > 0 ? true : false
          } else {
            return true
          }
        })
      } else {
        return true
      }
    }
  },

  /**
   *
   * @param {Object}
   *            args containingStencil: ORYX.Core.StencilSet.Stencil
   *            containingShape: ORYX.Core.AbstractShape containedStencil:
   *            ORYX.Core.StencilSet.Stencil containedShape: ORYX.Core.Shape
   */
  _canContain: function(args) {
    if (
      !args ||
      (!args.containingStencil && !args.containingShape) ||
      (!args.containedStencil && !args.containedShape)
    ) {
      return false
    }

    // init arguments
    if (args.containedShape) {
      args.containedStencil = args.containedShape.getStencil()
    }

    if (args.containingShape) {
      args.containingStencil = args.containingShape.getStencil()
    }

    //		if(args.containingShape) {
    //			if(args.containingShape instanceof ORYX.Core.Edge) {
    //				// edges cannot contain other shapes
    //				return false;
    //			}
    //		}

    var result

    // check containment rules
    result = args.containingStencil.roles().any(
      function(role) {
        var roles = this._containmentRules.get(role)
        if (roles) {
          return roles.any(function(role) {
            return args.containedStencil.roles().member(role)
          })
        } else {
          return false
        }
      }.bind(this)
    )

    return result
  },

  /** End containment rules' methods */

  /** Begin morphing rules' methods */

  /**
   *
   * @param {Object}
   *           args
   *            stencil: ORYX.Core.StencilSet.Stencil | undefined
   *            shape: ORYX.Core.Shape | undefined
   *
   * At least stencil or shape has to be specified
   *
   * @return {Array} Array of stencils that the passed stencil/shape can be
   *         transformed to (including the current stencil itself)
   */
  morphStencils: function(args) {
    // check arguments
    if (!args.stencil && !args.shape) {
      return []
    }

    // init arguments
    if (args.shape) {
      args.stencil = args.shape.getStencil()
    }

    var _morphStencils = []
    args.stencil.roles().each(
      function(role) {
        this._cacheMorph(role).each(function(stencil) {
          _morphStencils.push(stencil)
        })
      }.bind(this)
    )

    var baseMorphs = this.baseMorphs()
    // BaseMorphs should be in the front of the array
    _morphStencils = _morphStencils.uniq().sort(function(a, b) {
      return baseMorphs.include(a) && !baseMorphs.include(b)
        ? -1
        : baseMorphs.include(b) && !baseMorphs.include(a)
        ? 1
        : 0
    })
    return _morphStencils
  },

  /**
   * @return {Array} An array of all base morph stencils
   */
  baseMorphs: function() {
    var _baseMorphs = []
    this._morphingRules.each(function(pair) {
      pair.value.each(function(baseMorph) {
        _baseMorphs.push(baseMorph)
      })
    })
    return _baseMorphs
  },

  /**
   * Returns true if there are morphing rules defines
   * @return {boolean}
   */
  containsMorphingRules: function() {
    return this._stencilSets.any(function(ss) {
      return !!ss.jsonRules().morphingRules
    })
  },

  /**
   *
   * @param {Object}
   *            args
   *            sourceStencil:
   *            ORYX.Core.StencilSet.Stencil | undefined
   *            sourceShape:
   *            ORYX.Core.Node |undefined
   *            targetStencil:
   *            ORYX.Core.StencilSet.Stencil | undefined
   *            targetShape:
   *            ORYX.Core.Node |undefined
   *
   *
   * @return {Stencil} Returns, the stencil for the connecting edge
   * or null if connection is not possible
   */
  connectMorph: function(args) {
    // check arguments
    if (!args || (!args.sourceShape && !args.sourceStencil && !args.targetShape && !args.targetStencil)) {
      return false
    }

    // init arguments
    if (args.sourceShape) {
      args.sourceStencil = args.sourceShape.getStencil()
    }
    if (args.targetShape) {
      args.targetStencil = args.targetShape.getStencil()
    }

    var incoming = this.incomingEdgeStencils(args)
    var outgoing = this.outgoingEdgeStencils(args)

    var edgeStencils = incoming.select(function(e) {
      return outgoing.member(e)
    }) // intersection of sets
    var baseEdgeStencils = this.baseMorphs().select(function(e) {
      return edgeStencils.member(e)
    }) // again: intersection of sets

    if (baseEdgeStencils.size() > 0) return baseEdgeStencils[0]
    // return any of the possible base morphs
    else if (edgeStencils.size() > 0) return edgeStencils[0] // return any of the possible stencils

    return null //connection not possible
  },

  /**
   * Return true if the stencil should be located in the shape menu
   * @param {ORYX.Core.StencilSet.Stencil} morph
   * @return {Boolean} Returns true if the morphs in the morph group of the
   * specified morph shall be displayed in the shape menu
   */
  showInShapeMenu: function(stencil) {
    return this._stencilSets.any(function(ss) {
      return ss.jsonRules().morphingRules.any(function(r) {
        return stencil.roles().include(ss.namespace() + r.role) && r.showInShapeMenu !== false
      })
    })
  },

  preserveBounds: function(stencil) {
    return this._stencilSets.any(function(ss) {
      return ss.jsonRules().morphingRules.any(function(r) {
        return stencil.roles().include(ss.namespace() + r.role) && r.preserveBounds
      })
    })
  },

  /** End morphing rules' methods */

  /** Begin layouting rules' methods */

  /**
   * Returns a set on "in" and "out" layouting rules for a given shape
   * @param {Object} shape
   * @param {Object} edgeShape (Optional)
   * @return {Object} "in" and "out" with a default value of {"t":1, "r":1, "b":1, "r":1} if not specified in the json
   */
  getLayoutingRules: function(shape, edgeShape) {
    if (!shape || !(shape instanceof ORYX.Core.Shape)) {
      return
    }

    var layout = { in: {}, out: {} }

    var parseValues = function(o, v) {
      if (o && o[v]) {
        ;['t', 'r', 'b', 'l'].each(function(d) {
          layout[v][d] = Math.max(o[v][d], layout[v][d] || 0)
        })
      }
      if (o && o[v + 's'] instanceof Array) {
        ;['t', 'r', 'b', 'l'].each(
          function(d) {
            var defaultRule = o[v + 's'].find(function(e) {
              return !e.edgeRole
            })
            var edgeRule
            if (edgeShape instanceof ORYX.Core.Edge) {
              edgeRule = o[v + 's'].find(
                function(e) {
                  return this._hasRole(edgeShape, e.edgeRole)
                }.bind(this)
              )
            }
            layout[v][d] = Math.max(edgeRule ? edgeRule[d] : defaultRule[d], layout[v][d] || 0)
          }.bind(this)
        )
      }
    }.bind(this)

    // For each role
    shape
      .getStencil()
      .roles()
      .each(
        function(role) {
          // check if there are layout information
          if (this._layoutRules[role]) {
            // if so, parse those information to the 'layout' variable
            parseValues(this._layoutRules[role], 'in')
            parseValues(this._layoutRules[role], 'out')
          }
        }.bind(this)
      )

    // Make sure, that every attribute has an value,
    // otherwise set 1
    ;['in', 'out'].each(function(v) {
      ;['t', 'r', 'b', 'l'].each(function(d) {
        layout[v][d] = layout[v][d] !== undefined ? layout[v][d] : 1
      })
    })

    return layout
  },

  /** End layouting rules' methods */

  /** Helper methods */

  /**
   * Checks wether a shape contains the given role or the role is equal the stencil id
   * @param {ORYX.Core.Shape} shape
   * @param {String} role
   */
  _hasRole: function(shape, role) {
    if (!(shape instanceof ORYX.Core.Shape) || !role) {
      return
    }
    var isRole = shape
      .getStencil()
      .roles()
      .any(function(r) {
        return r == role
      })

    return isRole || shape.getStencil().id() == shape.getStencil().namespace() + role
  },

  /**
   *
   * @param {String}
   *            role
   *
   * @return {Array} Returns an array of stencils that can act as role.
   */
  _stencilsWithRole: function(role) {
    return this._stencils.findAll(function(stencil) {
      return stencil.roles().member(role) ? true : false
    })
  },

  /**
   *
   * @param {String}
   *            role
   *
   * @return {Array} Returns an array of stencils that can act as role and
   *         have the type 'edge'.
   */
  _edgesWithRole: function(role) {
    return this._stencils.findAll(function(stencil) {
      return stencil.roles().member(role) && stencil.type() === 'edge' ? true : false
    })
  },

  /**
   *
   * @param {String}
   *            role
   *
   * @return {Array} Returns an array of stencils that can act as role and
   *         have the type 'node'.
   */
  _nodesWithRole: function(role) {
    return this._stencils.findAll(function(stencil) {
      return stencil.roles().member(role) && stencil.type() === 'node' ? true : false
    })
  },

  /**
   *
   * @param {ORYX.Core.StencilSet.Stencil}
   *            parent
   * @param {ORYX.Core.StencilSet.Stencil}
   *            child
   *
   * @returns {Boolean} Returns the maximum occurrence of shapes of the
   *          stencil's type inside the parent.
   */
  _getMaximumOccurrence: function(parent, child) {
    var max
    child.roles().each(
      function(role) {
        var cardRule = this._cardinalityRules.get(role)
        if (cardRule && cardRule.maximumOccurrence) {
          if (max) {
            max = Math.min(max, cardRule.maximumOccurrence)
          } else {
            max = cardRule.maximumOccurrence
          }
        }
      }.bind(this)
    )

    return max
  },

  /**
   *
   * @param {Object}
   *            args sourceStencil: ORYX.Core.Node edgeStencil:
   *            ORYX.Core.StencilSet.Stencil
   *
   * @return {Boolean} Returns, the maximum number of outgoing edges of the
   *         type specified by edgeStencil of the sourceShape.
   */
  _getMaximumNumberOfOutgoingEdge: function(args) {
    if (!args || !args.sourceStencil || !args.edgeStencil) {
      return false
    }

    var max
    args.sourceStencil.roles().each(
      function(role) {
        var cardRule = this._cardinalityRules.get(role)

        if (cardRule && cardRule.outgoingEdges) {
          args.edgeStencil.roles().each(function(edgeRole) {
            var oe = cardRule.outgoingEdges[edgeRole]

            if (oe && oe.maximum) {
              if (max) {
                max = Math.min(max, oe.maximum)
              } else {
                max = oe.maximum
              }
            }
          })
        }
      }.bind(this)
    )

    return max
  },

  /**
   *
   * @param {Object}
   *            args targetStencil: ORYX.Core.StencilSet.Stencil edgeStencil:
   *            ORYX.Core.StencilSet.Stencil
   *
   * @return {Boolean} Returns the maximum number of incoming edges of the
   *         type specified by edgeStencil of the targetShape.
   */
  _getMaximumNumberOfIncomingEdge: function(args) {
    if (!args || !args.targetStencil || !args.edgeStencil) {
      return false
    }

    var max
    args.targetStencil.roles().each(
      function(role) {
        var cardRule = this._cardinalityRules[role]
        if (cardRule && cardRule.incomingEdges) {
          args.edgeStencil.roles().each(function(edgeRole) {
            var ie = cardRule.incomingEdges[edgeRole]
            if (ie && ie.maximum) {
              if (max) {
                max = Math.min(max, ie.maximum)
              } else {
                max = ie.maximum
              }
            }
          })
        }
      }.bind(this)
    )

    return max
  },

  /**
   *
   * @param {ORYX.Core.StencilSet.Stencil}
   *            edgeStencil
   *
   * @return {Hash} Returns a hash map of all connection rules for
   *         edgeStencil.
   */
  _getConnectionRulesOfEdgeStencil: function(edgeStencil) {
    var edgeRules = new Hash()
    edgeStencil.roles().each(
      function(role) {
        if (this._connectionRules.get(role)) {
          this._connectionRules.get(role).each(function(cr) {
            if (edgeRules.get(cr.key)) {
              edgeRules.set(cr.key, edgeRules.get([cr.key]).concat(cr.value))
            } else {
              edgeRules.set(cr.key, cr.value)
            }
          })
        }
      }.bind(this)
    )

    return edgeRules
  },

  _isRoleOfOtherNamespace: function(role) {
    return role.indexOf('#') > 0
  },

  toString: function() {
    return 'Rules'
  }
}
ORYX.Core.StencilSet.Rules = Clazz.extend(ORYX.Core.StencilSet.Rules)
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

/**
 * Init namespace
 */
if (!ORYX) {
  var ORYX = {}
}
if (!ORYX.Core) {
  ORYX.Core = {}
}
if (!ORYX.Core.StencilSet) {
  ORYX.Core.StencilSet = {}
}

/**
 * This class represents a stencil set. It offers methods for accessing
 *  the attributes of the stencil set description JSON file and the stencil set's
 *  stencils.
 */
ORYX.Core.StencilSet.StencilSet = Clazz.extend({
  /**
   * Constructor
   * @param source {URL} A reference to the stencil set specification.
   *
   */
  construct: function(baseUrl, data) {
    this._extensions = new Hash()

    this._baseUrl = baseUrl
    this._jsonObject = {}

    this._stencils = new Hash()
    this._availableStencils = new Hash()
    this._init(data)
  },

  /**
   * Finds a root stencil in this stencil set. There may be many of these. If
   * there are, the first one found will be used. In Firefox, this is the
   * topmost definition in the stencil set description file.
   */
  findRootStencilName: function() {
    // find any stencil that may be root.
    var rootStencil = this._stencils.values().find(function(stencil) {
      return stencil._jsonStencil.mayBeRoot
    })

    // if there is none, just guess the first.
    if (!rootStencil) {
      ORYX.Log.warn('Did not find any stencil that may be root. Taking a guess.')
      rootStencil = this._stencils.values()[0]
    }

    // return its id.
    return rootStencil.id()
  },

  /**
   * @param {ORYX.Core.StencilSet.StencilSet} stencilSet
   * @return {Boolean} True, if stencil set has the same namespace.
   */
  equals: function(stencilSet) {
    return this.namespace() === stencilSet.namespace()
  },

  /**
   *
   * @param {Oryx.Core.StencilSet.Stencil} rootStencil If rootStencil is defined, it only returns stencils
   * 			that could be (in)direct child of that stencil.
   */
  stencils: function(rootStencil, rules, sortByGroup) {
    if (rootStencil && rules) {
      var stencils = this._availableStencils.values()
      var containers = [rootStencil]
      var checkedContainers = []

      var result = []

      while (containers.size() > 0) {
        var container = containers.pop()
        checkedContainers.push(container)
        var children = stencils.findAll(function(stencil) {
          var args = {
            containingStencil: container,
            containedStencil: stencil
          }
          return rules.canContain(args)
        })
        for (var i = 0; i < children.size(); i++) {
          if (!checkedContainers.member(children[i])) {
            containers.push(children[i])
          }
        }
        result = result.concat(children).uniq()
      }

      // Sort the result to the origin order
      result = result.sortBy(function(stencil) {
        return stencils.indexOf(stencil)
      })

      if (sortByGroup) {
        result = result.sortBy(function(stencil) {
          return stencil.groups().first()
        })
      }

      var edges = stencils.findAll(function(stencil) {
        return stencil.type() == 'edge'
      })
      result = result.concat(edges)

      return result
    } else {
      if (sortByGroup) {
        return this._availableStencils.values().sortBy(function(stencil) {
          return stencil.groups().first()
        })
      } else {
        return this._availableStencils.values()
      }
    }
  },

  nodes: function() {
    return this._availableStencils.values().findAll(function(stencil) {
      return stencil.type() === 'node'
    })
  },

  edges: function() {
    return this._availableStencils.values().findAll(function(stencil) {
      return stencil.type() === 'edge'
    })
  },

  stencil: function(id) {
    return this._stencils.get(id)
  },

  title: function() {
    return ORYX.Core.StencilSet.getTranslation(this._jsonObject, 'title')
  },

  description: function() {
    return ORYX.Core.StencilSet.getTranslation(this._jsonObject, 'description')
  },

  namespace: function() {
    return this._jsonObject ? this._jsonObject.namespace : null
  },

  jsonRules: function() {
    return this._jsonObject ? this._jsonObject.rules : null
  },

  source: function() {
    return this._source
  },

  extensions: function() {
    return this._extensions
  },

  addExtension: function(url) {
    new Ajax.Request(url, {
      method: 'GET',
      asynchronous: false,
      onSuccess: function(transport) {
        this.addExtensionDirectly(transport.responseText)
      }.bind(this),
      onFailure: function(transport) {
        ORYX.Log.debug('Loading stencil set extension file failed. The request returned an error.' + transport)
      }.bind(this),
      onException: function(transport) {
        ORYX.Log.debug('Loading stencil set extension file failed. The request returned an error.' + transport)
      }.bind(this)
    })
  },

  addExtensionDirectly: function(str) {
    try {
      eval('var jsonExtension = ' + str)

      if (!jsonExtension['extends'].endsWith('#')) jsonExtension['extends'] += '#'

      if (jsonExtension['extends'] == this.namespace()) {
        this._extensions.set(jsonExtension.namespace, jsonExtension)

        var defaultPosition = this._stencils.keys().size()
        //load new stencils
        if (jsonExtension.stencils) {
          $A(jsonExtension.stencils).each(
            function(stencil) {
              defaultPosition++
              var oStencil = new ORYX.Core.StencilSet.Stencil(
                stencil,
                this.namespace(),
                this._baseUrl,
                this,
                undefined,
                defaultPosition
              )
              this._stencils.set(oStencil.id(), oStencil)
              this._availableStencils.set(oStencil.id(), oStencil)
            }.bind(this)
          )
        }

        //load additional properties
        if (jsonExtension.properties) {
          var stencils = this._stencils.values()

          stencils.each(
            function(stencil) {
              var roles = stencil.roles()

              jsonExtension.properties.each(function(prop) {
                prop.roles.any(function(role) {
                  role = jsonExtension['extends'] + role
                  if (roles.member(role)) {
                    prop.properties.each(function(property) {
                      stencil.addProperty(property, jsonExtension.namespace)
                    })

                    return true
                  } else return false
                })
              })
            }.bind(this)
          )
        }

        //remove stencil properties
        if (jsonExtension.removeproperties) {
          jsonExtension.removeproperties.each(
            function(remprop) {
              var stencil = this.stencil(jsonExtension['extends'] + remprop.stencil)
              if (stencil) {
                remprop.properties.each(function(propId) {
                  stencil.removeProperty(propId)
                })
              }
            }.bind(this)
          )
        }

        //remove stencils
        if (jsonExtension.removestencils) {
          $A(jsonExtension.removestencils).each(
            function(remstencil) {
              delete this._availableStencils[jsonExtension['extends'] + remstencil]
            }.bind(this)
          )
        }
      }
    } catch (e) {
      ORYX.Log.debug('StencilSet.addExtension: Something went wrong when initialising the stencil set extension. ' + e)
    }
  },

  removeExtension: function(namespace) {
    var jsonExtension = this._extensions[namespace]
    if (jsonExtension) {
      //unload extension's stencils
      if (jsonExtension.stencils) {
        $A(jsonExtension.stencils).each(
          function(stencil) {
            var oStencil = new ORYX.Core.StencilSet.Stencil(stencil, this.namespace(), this._baseUrl, this)
            this._stencils.unset(oStencil.id())
            this._availableStencils.unset(oStencil.id())
          }.bind(this)
        )
      }

      //unload extension's properties
      if (jsonExtension.properties) {
        var stencils = this._stencils.values()

        stencils.each(
          function(stencil) {
            var roles = stencil.roles()

            jsonExtension.properties.each(function(prop) {
              prop.roles.any(function(role) {
                role = jsonExtension['extends'] + role
                if (roles.member(role)) {
                  prop.properties.each(function(property) {
                    stencil.removeProperty(property.id)
                  })

                  return true
                } else return false
              })
            })
          }.bind(this)
        )
      }

      //restore removed stencil properties
      if (jsonExtension.removeproperties) {
        jsonExtension.removeproperties.each(
          function(remprop) {
            var stencil = this.stencil(jsonExtension['extends'] + remprop.stencil)
            if (stencil) {
              var stencilJson = $A(this._jsonObject.stencils).find(function(s) {
                return s.id == stencil.id()
              })
              remprop.properties.each(
                function(propId) {
                  var propertyJson = $A(stencilJson.properties).find(function(p) {
                    return p.id == propId
                  })
                  stencil.addProperty(propertyJson, this.namespace())
                }.bind(this)
              )
            }
          }.bind(this)
        )
      }

      //restore removed stencils
      if (jsonExtension.removestencils) {
        $A(jsonExtension.removestencils).each(
          function(remstencil) {
            var sId = jsonExtension['extends'] + remstencil
            this._availableStencils.set(sId, this._stencils.get(sId))
          }.bind(this)
        )
      }
    }
    delete this._extensions[namespace]
  },

  __handleStencilset: function(response) {
    this._jsonObject = response

    // assert it was parsed.
    if (!this._jsonObject) {
      throw 'Error evaluating stencilset. It may be corrupt.'
    }

    with (this._jsonObject) {
      // assert there is a namespace.
      if (!namespace || namespace === '') throw 'Namespace definition missing in stencilset.'

      if (!(stencils instanceof Array)) throw 'Stencilset corrupt.'

      // assert namespace ends with '#'.
      if (!namespace.endsWith('#')) namespace = namespace + '#'

      // assert title and description are strings.
      if (!title) title = ''
      if (!description) description = ''
    }
  },

  /**
   * This method is called when the HTTP request to get the requested stencil
   * set succeeds. The response is supposed to be a JSON representation
   * according to the stencil set specification.
   * @param {Object} response The JSON representation according to the
   * 			stencil set specification.
   */
  _init: function(response) {
    // init and check consistency.
    this.__handleStencilset(response)

    var pps = new Hash()

    // init property packages
    if (this._jsonObject.propertyPackages) {
      $A(this._jsonObject.propertyPackages).each(
        function(pp) {
          pps.set(pp.name, pp.properties)
        }.bind(this)
      )
    }

    var defaultPosition = 0

    // init each stencil
    $A(this._jsonObject.stencils).each(
      function(stencil) {
        defaultPosition++

        // instantiate normally.
        var oStencil = new ORYX.Core.StencilSet.Stencil(
          stencil,
          this.namespace(),
          this._baseUrl,
          this,
          pps,
          defaultPosition
        )
        this._stencils.set(oStencil.id(), oStencil)
        this._availableStencils.set(oStencil.id(), oStencil)
      }.bind(this)
    )
  },

  _cancelInit: function(response) {
    this.errornous = true
  },

  toString: function() {
    return 'StencilSet ' + this.title() + ' (' + this.namespace() + ')'
  }
})
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

/**
 * Init namespace
 */
if (!ORYX) {
  var ORYX = {}
}
if (!ORYX.Core) {
  ORYX.Core = {}
}
if (!ORYX.Core.StencilSet) {
  ORYX.Core.StencilSet = {}
}

/**
 * Class StencilSets
 * uses Prototpye 1.5.0
 * uses Inheritance
 *
 * Singleton
 */
//storage for loaded stencil sets by namespace
ORYX.Core.StencilSet._stencilSetsByNamespace = new Hash()

//storage for stencil sets by url
ORYX.Core.StencilSet._stencilSetsByUrl = new Hash()

//storage for stencil set namespaces by editor instances
ORYX.Core.StencilSet._StencilSetNSByEditorInstance = new Hash()

//storage for rules by editor instances
ORYX.Core.StencilSet._rulesByEditorInstance = new Hash()

/**
 *
 * @param {String} editorId
 *
 * @return {Hash} Returns a hash map with all stencil sets that are loaded by
 * 					the editor with the editorId.
 */
ORYX.Core.StencilSet.stencilSets = function(editorId) {
  var stencilSetNSs = ORYX.Core.StencilSet._StencilSetNSByEditorInstance.get(editorId)
  var stencilSets = new Hash()
  if (stencilSetNSs) {
    stencilSetNSs.each(function(stencilSetNS) {
      var stencilSet = ORYX.Core.StencilSet.stencilSet(stencilSetNS)
      stencilSets.set(stencilSet.namespace(), stencilSet)
    })
  }
  return stencilSets
}

/**
 *
 * @param {String} namespace
 *
 * @return {ORYX.Core.StencilSet.StencilSet} Returns the stencil set with the specified
 * 										namespace.
 *
 * The method can handle namespace strings like
 *  http://www.example.org/stencilset
 *  http://www.example.org/stencilset#
 *  http://www.example.org/stencilset#ANode
 */
ORYX.Core.StencilSet.stencilSet = function(namespace) {
  ORYX.Log.trace('Getting stencil set %0', namespace)
  var splitted = namespace.split('#', 1)
  if (splitted.length === 1) {
    ORYX.Log.trace('Getting stencil set %0', splitted[0])
    return ORYX.Core.StencilSet._stencilSetsByNamespace.get(splitted[0] + '#')
  } else {
    return undefined
  }
}

/**
 *
 * @param {String} id
 *
 * @return {ORYX.Core.StencilSet.Stencil} Returns the stencil specified by the id.
 *
 * The id must be unique and contains the namespace of the stencil's stencil set.
 * e.g. http://www.example.org/stencilset#ANode
 */
ORYX.Core.StencilSet.stencil = function(id) {
  ORYX.Log.trace('Getting stencil for %0', id)
  var ss = ORYX.Core.StencilSet.stencilSet(id)
  if (ss) {
    return ss.stencil(id)
  } else {
    ORYX.Log.trace('Cannot fild stencil for %0', id)
    return undefined
  }
}

/**
 *
 * @param {String} editorId
 *
 * @return {ORYX.Core.StencilSet.Rules} Returns the rules object for the editor
 * 									specified by its editor id.
 */
ORYX.Core.StencilSet.rules = function(editorId) {
  if (!ORYX.Core.StencilSet._rulesByEditorInstance.get(editorId)) {
    ORYX.Core.StencilSet._rulesByEditorInstance.set(editorId, new ORYX.Core.StencilSet.Rules())
  }
  return ORYX.Core.StencilSet._rulesByEditorInstance.get(editorId)
}

/**
 *
 * @param {String} url
 * @param {String} editorId
 *
 * Loads a stencil set from url, if it is not already loaded.
 * It also stores which editor instance loads the stencil set and
 * initializes the Rules object for the editor instance.
 */
ORYX.Core.StencilSet.loadStencilSet = function(url, stencilSet, editorId) {
  //store stencil set
  ORYX.Core.StencilSet._stencilSetsByNamespace.set(stencilSet.namespace(), stencilSet)

  //store stencil set by url
  ORYX.Core.StencilSet._stencilSetsByUrl.set(url, stencilSet)

  var namespace = stencilSet.namespace()

  //store which editorInstance loads the stencil set
  if (ORYX.Core.StencilSet._StencilSetNSByEditorInstance.get(editorId)) {
    ORYX.Core.StencilSet._StencilSetNSByEditorInstance.get(editorId).push(namespace)
  } else {
    ORYX.Core.StencilSet._StencilSetNSByEditorInstance.set(editorId, [namespace])
  }

  //store the rules for the editor instance
  if (ORYX.Core.StencilSet._rulesByEditorInstance.get(editorId)) {
    ORYX.Core.StencilSet._rulesByEditorInstance.get(editorId).initializeRules(stencilSet)
  } else {
    var rules = new ORYX.Core.StencilSet.Rules()
    rules.initializeRules(stencilSet)
    ORYX.Core.StencilSet._rulesByEditorInstance.set(editorId, rules)
  }
}

/**
 * Returns the translation of an attribute in jsonObject specified by its name
 * according to navigator.language
 */
ORYX.Core.StencilSet.getTranslation = function(jsonObject, name) {
  var lang = ORYX.I18N.Language.toLowerCase()

  var result = jsonObject[name + '_' + lang]

  if (result) return result

  result = jsonObject[name + '_' + lang.substr(0, 2)]

  if (result) return result

  return jsonObject[name]
}
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

/**
 * Init namespaces
 */
if (!ORYX) {
  var ORYX = {}
}
if (!ORYX.Core) {
  ORYX.Core = {}
}

/**
 * @classDescription With Bounds you can set and get position and size of UIObjects.
 */
ORYX.Core.Command = Clazz.extend({
  /**
   * Constructor
   */
  construct: function() {},

  execute: function() {
    throw 'Command.execute() has to be implemented!'
  },

  rollback: function() {
    throw 'Command.rollback() has to be implemented!'
  }
})
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

/**
 * Init namespaces
 */
if (!ORYX) {
  var ORYX = {}
}
if (!ORYX.Core) {
  ORYX.Core = {}
}

/**
 * @classDescription With Bounds you can set and get position and size of UIObjects.
 */
ORYX.Core.Bounds = {
  /**
   * Constructor
   */
  construct: function() {
    this._changedCallbacks = [] //register a callback with changedCallacks.push(this.method.bind(this));
    this.a = {}
    this.b = {}
    this.set.apply(this, arguments)
    this.suspendChange = false
    this.changedWhileSuspend = false
  },

  /**
   * Calls all registered callbacks.
   */
  _changed: function(sizeChanged) {
    if (!this.suspendChange) {
      this._changedCallbacks.each(
        function(callback) {
          callback(this, sizeChanged)
        }.bind(this)
      )
      this.changedWhileSuspend = false
    } else this.changedWhileSuspend = true
  },

  /**
   * Registers a callback that is called, if the bounds changes.
   * @param callback {Function} The callback function.
   */
  registerCallback: function(callback) {
    if (!this._changedCallbacks.member(callback)) {
      this._changedCallbacks.push(callback)
    }
  },

  /**
   * Unregisters a callback.
   * @param callback {Function} The callback function.
   */
  unregisterCallback: function(callback) {
    this._changedCallbacks = this._changedCallbacks.without(callback)
  },

  /**
   * Sets position and size of the shape dependent of four coordinates
   * (set(ax, ay, bx, by);), two points (set({x: ax, y: ay}, {x: bx, y: by});)
   * or one bound (set({a: {x: ax, y: ay}, b: {x: bx, y: by}});).
   */
  set: function() {
    var changed = false

    switch (arguments.length) {
      case 1:
        if (this.a.x !== arguments[0].a.x) {
          changed = true
          this.a.x = arguments[0].a.x
        }
        if (this.a.y !== arguments[0].a.y) {
          changed = true
          this.a.y = arguments[0].a.y
        }
        if (this.b.x !== arguments[0].b.x) {
          changed = true
          this.b.x = arguments[0].b.x
        }
        if (this.b.y !== arguments[0].b.y) {
          changed = true
          this.b.y = arguments[0].b.y
        }
        break

      case 2:
        var ax = Math.min(arguments[0].x, arguments[1].x)
        var ay = Math.min(arguments[0].y, arguments[1].y)
        var bx = Math.max(arguments[0].x, arguments[1].x)
        var by = Math.max(arguments[0].y, arguments[1].y)
        if (this.a.x !== ax) {
          changed = true
          this.a.x = ax
        }
        if (this.a.y !== ay) {
          changed = true
          this.a.y = ay
        }
        if (this.b.x !== bx) {
          changed = true
          this.b.x = bx
        }
        if (this.b.y !== by) {
          changed = true
          this.b.y = by
        }
        break

      case 4:
        var ax = Math.min(arguments[0], arguments[2])
        var ay = Math.min(arguments[1], arguments[3])
        var bx = Math.max(arguments[0], arguments[2])
        var by = Math.max(arguments[1], arguments[3])
        if (this.a.x !== ax) {
          changed = true
          this.a.x = ax
        }
        if (this.a.y !== ay) {
          changed = true
          this.a.y = ay
        }
        if (this.b.x !== bx) {
          changed = true
          this.b.x = bx
        }
        if (this.b.y !== by) {
          changed = true
          this.b.y = by
        }
        break
    }

    if (changed) {
      this._changed(true)
    }
  },

  /**
   * Moves the bounds so that the point p will be the new upper left corner.
   * @param {Point} p
   * or
   * @param {Number} x
   * @param {Number} y
   */
  moveTo: function() {
    var currentPosition = this.upperLeft()
    switch (arguments.length) {
      case 1:
        this.moveBy({
          x: arguments[0].x - currentPosition.x,
          y: arguments[0].y - currentPosition.y
        })
        break
      case 2:
        this.moveBy({
          x: arguments[0] - currentPosition.x,
          y: arguments[1] - currentPosition.y
        })
        break
      default:
      //TODO error
    }
  },

  /**
   * Moves the bounds relatively by p.
   * @param {Point} p
   * or
   * @param {Number} x
   * @param {Number} y
   *
   */
  moveBy: function() {
    var changed = false

    switch (arguments.length) {
      case 1:
        var p = arguments[0]
        if (p.x !== 0 || p.y !== 0) {
          changed = true
          this.a.x += p.x
          this.b.x += p.x
          this.a.y += p.y
          this.b.y += p.y
        }
        break
      case 2:
        var x = arguments[0]
        var y = arguments[1]
        if (x !== 0 || y !== 0) {
          changed = true
          this.a.x += x
          this.b.x += x
          this.a.y += y
          this.b.y += y
        }
        break
      default:
      //TODO error
    }

    if (changed) {
      this._changed()
    }
  },

  /***
   * Includes the bounds b into the current bounds.
   * @param {Bounds} b
   */
  include: function(b) {
    if (this.a.x === undefined && this.a.y === undefined && this.b.x === undefined && this.b.y === undefined) {
      return b
    }

    var cx = Math.min(this.a.x, b.a.x)
    var cy = Math.min(this.a.y, b.a.y)

    var dx = Math.max(this.b.x, b.b.x)
    var dy = Math.max(this.b.y, b.b.y)

    this.set(cx, cy, dx, dy)
  },

  /**
   * Relatively extends the bounds by p.
   * @param {Point} p
   */
  extend: function(p) {
    if (p.x !== 0 || p.y !== 0) {
      // this is over cross for the case that a and b have same coordinates.
      //((this.a.x > this.b.x) ? this.a : this.b).x += p.x;
      //((this.b.y > this.a.y) ? this.b : this.a).y += p.y;
      this.b.x += p.x
      this.b.y += p.y

      this._changed(true)
    }
  },

  /**
   * Widens the scope of the bounds by x.
   * @param {Number} x
   */
  widen: function(x) {
    if (x !== 0) {
      this.suspendChange = true
      this.moveBy({ x: -x, y: -x })
      this.extend({ x: 2 * x, y: 2 * x })
      this.suspendChange = false
      if (this.changedWhileSuspend) {
        this._changed(true)
      }
    }
  },

  /**
   * Returns the upper left corner's point regardless of the
   * bound delimiter points.
   */
  upperLeft: function() {
    var result = {}
    result.x = this.a.x
    result.y = this.a.y
    return result
  },

  /**
   * Returns the lower Right left corner's point regardless of the
   * bound delimiter points.
   */
  lowerRight: function() {
    var result = {}
    result.x = this.b.x
    result.y = this.b.y
    return result
  },

  /**
   * @return {Number} Width of bounds.
   */
  width: function() {
    return this.b.x - this.a.x
  },

  /**
   * @return {Number} Height of bounds.
   */
  height: function() {
    return this.b.y - this.a.y
  },

  /**
   * @return {Point} The center point of this bounds.
   */
  center: function() {
    var center = {}
    center.x = (this.a.x + this.b.x) / 2.0
    center.y = (this.a.y + this.b.y) / 2.0
    return center
  },

  /**
   * @return {Point} The center point of this bounds relative to upperLeft.
   */
  midPoint: function() {
    var midpoint = {}
    midpoint.x = (this.b.x - this.a.x) / 2.0
    midpoint.y = (this.b.y - this.a.y) / 2.0
    return midpoint
  },

  /**
   * Moves the center point of this bounds to the new position.
   * @param p {Point}
   * or
   * @param x {Number}
   * @param y {Number}
   */
  centerMoveTo: function() {
    var currentPosition = this.center()

    switch (arguments.length) {
      case 1:
        this.moveBy(arguments[0].x - currentPosition.x, arguments[0].y - currentPosition.y)
        break

      case 2:
        this.moveBy(arguments[0] - currentPosition.x, arguments[1] - currentPosition.y)
        break
    }
  },

  isIncluded: function(point, offset) {
    var pointX, pointY, offset

    // Get the the two Points
    switch (arguments.length) {
      case 1:
        pointX = arguments[0].x
        pointY = arguments[0].y
        offset = 0

        break
      case 2:
        if (arguments[0].x && arguments[0].y) {
          pointX = arguments[0].x
          pointY = arguments[0].y
          offset = Math.abs(arguments[1])
        } else {
          pointX = arguments[0]
          pointY = arguments[1]
          offset = 0
        }
        break
      case 3:
        pointX = arguments[0]
        pointY = arguments[1]
        offset = Math.abs(arguments[2])
        break
      default:
        throw 'isIncluded needs one, two or three arguments'
    }

    var ul = this.upperLeft()
    var lr = this.lowerRight()

    if (pointX >= ul.x - offset && pointX <= lr.x + offset && pointY >= ul.y - offset && pointY <= lr.y + offset)
      return true
    else return false
  },

  /**
   * @return {Bounds} A copy of this bounds.
   */
  clone: function() {
    //Returns a new bounds object without the callback
    // references of the original bounds
    return new ORYX.Core.Bounds(this)
  },

  toString: function() {
    return '( ' + this.a.x + ' | ' + this.a.y + ' )/( ' + this.b.x + ' | ' + this.b.y + ' )'
  },

  serializeForERDF: function() {
    return this.a.x + ',' + this.a.y + ',' + this.b.x + ',' + this.b.y
  }
}

ORYX.Core.Bounds = Clazz.extend(ORYX.Core.Bounds)
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

/**
 * Init namespaces
 */
if (!ORYX) {
  var ORYX = {}
}
if (!ORYX.Core) {
  ORYX.Core = {}
}

/**
 * @classDescription Abstract base class for all objects that have a graphical representation
 * within the editor.
 * @extends Clazz
 */
ORYX.Core.UIObject = {
  /**
   * Constructor of the UIObject class.
   */
  construct: function(options) {
    this.isChanged = true //Flag, if UIObject has been changed since last update.
    this.isResized = true
    this.isVisible = true //Flag, if UIObject's display attribute is set to 'inherit' or 'none'
    this.isSelectable = false //Flag, if UIObject is selectable.
    this.isResizable = false //Flag, if UIObject is resizable.
    this.isMovable = false //Flag, if UIObject is movable.

    this.id = ORYX.Editor.provideId() //get unique id
    this.parent = undefined //parent is defined, if this object is added to another uiObject.
    this.node = undefined //this is a reference to the SVG representation, either locally or in DOM.
    this.children = [] //array for all add uiObjects

    this.bounds = new ORYX.Core.Bounds() //bounds with undefined values

    this._changedCallback = this._changed.bind(this) //callback reference for calling _changed
    this.bounds.registerCallback(this._changedCallback) //set callback in bounds

    if (options && options.eventHandlerCallback) {
      this.eventHandlerCallback = options.eventHandlerCallback
    }
  },

  /**
   * Sets isChanged flag to true. Callback for the bounds object.
   */
  _changed: function(bounds, isResized) {
    this.isChanged = true
    if (this.bounds == bounds) this.isResized = isResized || this.isResized
  },

  /**
   * If something changed, this method calls the refresh method that must be implemented by subclasses.
   */
  update: function() {
    if (this.isChanged) {
      this.refresh()
      this.isChanged = false

      //call update of all children
      this.children.each(function(value) {
        value.update()
      })
    }
  },

  /**
   * Is called in update method, if isChanged is set to true. Sub classes should call the super class method.
   */
  refresh: function() {},

  /**
   * @return {Array} Array of all child UIObjects.
   */
  getChildren: function() {
    return this.children.clone()
  },

  /**
   * @return {Array} Array of all parent UIObjects.
   */
  getParents: function() {
    var parents = []
    var parent = this.parent
    while (parent) {
      parents.push(parent)
      parent = parent.parent
    }
    return parents
  },

  /**
   * Returns TRUE if the given parent is one of the UIObjects parents or the UIObject themselves, otherwise FALSE.
   * @param {UIObject} parent
   * @return {Boolean}
   */
  isParent: function(parent) {
    var cparent = this
    while (cparent) {
      if (cparent === parent) {
        return true
      }
      cparent = cparent.parent
    }
    return false
  },

  /**
   * @return {String} Id of this UIObject
   */
  getId: function() {
    return this.id
  },

  /**
   * Method for accessing child uiObjects by id.
   * @param {String} id
   * @param {Boolean} deep
   *
   * @return {UIObject} If found, it returns the UIObject with id.
   */
  getChildById: function(id, deep) {
    return this.children.find(function(uiObj) {
      if (uiObj.getId() === id) {
        return uiObj
      } else {
        if (deep) {
          var obj = uiObj.getChildById(id, deep)
          if (obj) {
            return obj
          }
        }
      }
    })
  },

  /**
   * Adds an UIObject to this UIObject and sets the parent of the
   * added UIObject. It is also added to the SVG representation of this
   * UIObject.
   * @param {UIObject} uiObject
   */
  add: function(uiObject) {
    //add uiObject, if it is not already a child of this object
    if (!this.children.member(uiObject)) {
      //if uiObject is child of another parent, remove it from that parent.
      if (uiObject.parent) {
        uiObject.remove(uiObject)
      }

      //add uiObject to children
      this.children.push(uiObject)

      //set parent reference
      uiObject.parent = this

      //add uiObject.node to this.node
      uiObject.node = this.node.appendChild(uiObject.node)

      //register callback to get informed, if child is changed
      uiObject.bounds.registerCallback(this._changedCallback)

      //uiObject.update();
    } else {
      ORYX.Log.info('add: ORYX.Core.UIObject is already a child of this object.')
    }
  },

  /**
   * Removes UIObject from this UIObject. The SVG representation will also
   * be removed from this UIObject's SVG representation.
   * @param {UIObject} uiObject
   */
  remove: function(uiObject) {
    //if uiObject is a child of this object, remove it.
    if (this.children.member(uiObject)) {
      //remove uiObject from children
      this.children = this._uiObjects.without(uiObject)

      //delete parent reference of uiObject
      uiObject.parent = undefined

      //delete uiObject.node from this.node
      uiObject.node = this.node.removeChild(uiObject.node)

      //unregister callback to get informed, if child is changed
      uiObject.bounds.unregisterCallback(this._changedCallback)
    } else {
      ORYX.Log.info('remove: ORYX.Core.UIObject is not a child of this object.')
    }
  },

  /**
   * Calculates absolute bounds of this UIObject.
   */
  absoluteBounds: function() {
    if (this.parent) {
      var absUL = this.absoluteXY()
      return new ORYX.Core.Bounds(absUL.x, absUL.y, absUL.x + this.bounds.width(), absUL.y + this.bounds.height())
    } else {
      return this.bounds.clone()
    }
  },

  /**
   * @return {Point} The absolute position of this UIObject.
   */
  absoluteXY: function() {
    if (this.parent) {
      var pXY = this.parent.absoluteXY()
      var result = {}
      result.x = pXY.x + this.bounds.upperLeft().x
      result.y = pXY.y + this.bounds.upperLeft().y
      return result
    } else {
      var result = {}
      result.x = this.bounds.upperLeft().x
      result.y = this.bounds.upperLeft().y
      return result
    }
  },

  /**
   * @return {Point} The absolute position from the Center of this UIObject.
   */
  absoluteCenterXY: function() {
    if (this.parent) {
      var pXY = this.parent.absoluteXY()
      var result = {}
      result.x = pXY.x + this.bounds.center().x
      result.y = pXY.y + this.bounds.center().y
      return result
    } else {
      var result = {}
      result.x = this.bounds.center().x
      result.y = this.bounds.center().y
      return result
    }
  },

  /**
   * Hides this UIObject and all its children.
   */
  hide: function() {
    this.node.setAttributeNS(null, 'display', 'none')
    this.isVisible = false
    this.children.each(function(uiObj) {
      uiObj.hide()
    })
  },

  /**
   * Enables visibility of this UIObject and all its children.
   */
  show: function() {
    this.node.setAttributeNS(null, 'display', 'inherit')
    this.isVisible = true
    this.children.each(function(uiObj) {
      uiObj.show()
    })
  },

  addEventHandlers: function(node) {
    node.addEventListener(ORYX.CONFIG.EVENT_MOUSEDOWN, this._delegateEvent.bind(this), false)
    node.addEventListener(ORYX.CONFIG.EVENT_MOUSEMOVE, this._delegateEvent.bind(this), false)
    node.addEventListener(ORYX.CONFIG.EVENT_MOUSEUP, this._delegateEvent.bind(this), false)
    node.addEventListener(ORYX.CONFIG.EVENT_MOUSEOVER, this._delegateEvent.bind(this), false)
    node.addEventListener(ORYX.CONFIG.EVENT_MOUSEOUT, this._delegateEvent.bind(this), false)
    node.addEventListener('click', this._delegateEvent.bind(this), false)
    node.addEventListener(ORYX.CONFIG.EVENT_DBLCLICK, this._delegateEvent.bind(this), false)
  },

  _delegateEvent: function(event) {
    if (this.eventHandlerCallback) {
      this.eventHandlerCallback(event, this)
    }
  },

  toString: function() {
    return 'UIObject ' + this.id
  }
}
ORYX.Core.UIObject = Clazz.extend(ORYX.Core.UIObject)
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

/**
 * Init namespaces
 */
if (!ORYX) {
  var ORYX = {}
}
if (!ORYX.Core) {
  ORYX.Core = {}
}

/**
 * Top Level uiobject.
 * @class ORYX.Core.AbstractShape
 * @extends ORYX.Core.UIObject
 */
ORYX.Core.AbstractShape = ORYX.Core.UIObject.extend(
  /** @lends ORYX.Core.AbstractShape.prototype */
  {
    /**
     * Constructor
     */
    construct: function(options, stencil, facade) {
      arguments.callee.$.construct.apply(this, arguments)

      this.resourceId = ORYX.Editor.provideId() //Id of resource in DOM

      // stencil reference
      this._stencil = stencil
      // if the stencil defines a super stencil that should be used for its instances, set it.
      if (this._stencil._jsonStencil.superId) {
        stencilId = this._stencil.id()
        superStencilId = stencilId.substring(0, stencilId.indexOf('#') + 1) + stencil._jsonStencil.superId
        stencilSet = this._stencil.stencilSet()
        this._stencil = stencilSet.stencil(superStencilId)
      }

      //Hash map for all properties. Only stores the values of the properties.
      this.properties = new Hash()
      this.propertiesChanged = new Hash()

      // List of properties which are not included in the stencilset,
      // but which gets (de)serialized
      this.hiddenProperties = new Hash()

      //Initialization of property map and initial value.
      this._stencil.properties().each(
        function(property) {
          var key = property.prefix() + '-' + property.id()
          this.properties.set(key, property.value())
          this.propertiesChanged.set(key, true)
        }.bind(this)
      )

      // if super stencil was defined, also regard stencil's properties:
      if (stencil._jsonStencil.superId) {
        stencil.properties().each(
          function(property) {
            var key = property.prefix() + '-' + property.id()
            var value = property.value()
            var oldValue = this.properties.get(key)
            this.properties.set(key, value)
            this.propertiesChanged.set(key, true)

            // Raise an event, to show that the property has changed
            // required for plugins like processLink.js
            //window.setTimeout( function(){

            this._delegateEvent({
              type: ORYX.CONFIG.EVENT_PROPERTY_CHANGED,
              name: key,
              value: value,
              oldValue: oldValue
            })

            //}.bind(this), 10)
          }.bind(this)
        )
      }
    },

    layout: function() {},

    /**
     * Returns the stencil object specifiing the type of the shape.
     */
    getStencil: function() {
      return this._stencil
    },

    /**
     *
     * @param {Object} resourceId
     */
    getChildShapeByResourceId: function(resourceId) {
      resourceId = ERDF.__stripHashes(resourceId)

      return this.getChildShapes(true).find(function(shape) {
        return shape.resourceId == resourceId
      })
    },
    /**
     *
     * @param {Object} deep
     * @param {Object} iterator
     */
    getChildShapes: function(deep, iterator) {
      var result = []

      this.children.each(function(uiObject) {
        if (uiObject instanceof ORYX.Core.Shape && uiObject.isVisible) {
          if (iterator) {
            iterator(uiObject)
          }
          result.push(uiObject)
          if (deep) {
            result = result.concat(uiObject.getChildShapes(deep, iterator))
          }
        }
      })

      return result
    },

    /**
     * @param {Object} shape
     * @return {boolean} true if any of shape's childs is given shape
     */
    hasChildShape: function(shape) {
      return this.getChildShapes().any(function(child) {
        return child === shape || child.hasChildShape(shape)
      })
    },

    /**
     *
     * @param {Object} deep
     * @param {Object} iterator
     */
    getChildNodes: function(deep, iterator) {
      var result = []

      this.children.each(function(uiObject) {
        if (uiObject instanceof ORYX.Core.Node && uiObject.isVisible) {
          if (iterator) {
            iterator(uiObject)
          }
          result.push(uiObject)
        }
        if (uiObject instanceof ORYX.Core.Shape) {
          if (deep) {
            result = result.concat(uiObject.getChildNodes(deep, iterator))
          }
        }
      })

      return result
    },

    /**
     *
     * @param {Object} deep
     * @param {Object} iterator
     */
    getChildEdges: function(deep, iterator) {
      var result = []

      this.children.each(function(uiObject) {
        if (uiObject instanceof ORYX.Core.Edge && uiObject.isVisible) {
          if (iterator) {
            iterator(uiObject)
          }
          result.push(uiObject)
        }
        if (uiObject instanceof ORYX.Core.Shape) {
          if (deep) {
            result = result.concat(uiObject.getChildEdges(deep, iterator))
          }
        }
      })

      return result
    },

    /**
     * Returns a sorted array of ORYX.Core.Node objects.
     * Ordered in z Order, the last object has the highest z Order.
     */
    //TODO deep iterator
    getAbstractShapesAtPosition: function() {
      var x, y
      switch (arguments.length) {
        case 1:
          x = arguments[0].x
          y = arguments[0].y
          break
        case 2: //two or more arguments
          x = arguments[0]
          y = arguments[1]
          break
        default:
          throw 'getAbstractShapesAtPosition needs 1 or 2 arguments!'
      }

      if (this.isPointIncluded(x, y)) {
        var result = []
        result.push(this)

        //check, if one child is at that position

        var childNodes = this.getChildNodes()
        var childEdges = this.getChildEdges()

        ;[childNodes, childEdges].each(function(ne) {
          var nodesAtPosition = new Hash()

          ne.each(function(node) {
            if (!node.isVisible) {
              return
            }
            var candidates = node.getAbstractShapesAtPosition(x, y)
            if (candidates.length > 0) {
              var nodesInZOrder = $A(node.node.parentNode.childNodes)
              var zOrderIndex = nodesInZOrder.indexOf(node.node)
              nodesAtPosition.set(zOrderIndex, candidates)
            }
          })

          nodesAtPosition
            .keys()
            .sort()
            .each(function(key) {
              result = result.concat(nodesAtPosition.get(key))
            })
        })

        return result
      } else {
        return []
      }
    },

    /**
     *
     * @param key {String} Must be 'prefix-id' of property
     * @param value {Object} Can be of type String or Number according to property type.
     */
    setProperty: function(key, value, force) {
      var oldValue = this.properties.get(key)
      if (oldValue !== value || force === true) {
        this.properties.set(key, value)
        this.propertiesChanged.set(key, true)
        this._changed()

        // Raise an event, to show that the property has changed
        //window.setTimeout( function(){

        if (!this._isInSetProperty) {
          this._isInSetProperty = true

          this._delegateEvent({
            type: ORYX.CONFIG.EVENT_PROPERTY_CHANGED,
            elements: [this],
            name: key,
            value: value,
            oldValue: oldValue
          })

          delete this._isInSetProperty
        }
        //}.bind(this), 10)
      }
    },

    /**
     * Returns TRUE if one of the properties is flagged as dirty
     * @return {boolean}
     */
    isPropertyChanged: function() {
      return this.propertiesChanged.any(function(property) {
        return property.value
      })
    },

    /**
     *
     * @param {String} Must be 'prefix-id' of property
     * @param {Object} Can be of type String or Number according to property type.
     */
    setHiddenProperty: function(key, value) {
      // IF undefined, Delete
      if (value === undefined) {
        this.hiddenProperties.unset(key)
        return
      }
      var oldValue = this.hiddenProperties.get(key)
      if (oldValue !== value) {
        this.hiddenProperties.set(key, value)
      }
    },
    /**
     * Calculate if the point is inside the Shape
     * @param {Point}
     */
    isPointIncluded: function(pointX, pointY, absoluteBounds) {
      var absBounds = absoluteBounds ? absoluteBounds : this.absoluteBounds()
      return absBounds.isIncluded(pointX, pointY)
    },

    /**
     * Get the serialized object
     * return Array with hash-entrees (prefix, name, value)
     * Following values will given:
     * 		Type
     * 		Properties
     */
    serialize: function() {
      var serializedObject = []

      // Add the type
      serializedObject.push({ name: 'type', prefix: 'oryx', value: this.getStencil().id(), type: 'literal' })

      // Add hidden properties
      this.hiddenProperties.each(
        function(prop) {
          serializedObject.push({
            name: prop.key.replace('oryx-', ''),
            prefix: 'oryx',
            value: prop.value,
            type: 'literal'
          })
        }.bind(this)
      )

      // Add all properties
      this.getStencil()
        .properties()
        .each(
          function(property) {
            var prefix = property.prefix() // Get prefix
            var name = property.id() // Get name

            //if(typeof this.properties[prefix+'-'+name] == 'boolean' || this.properties[prefix+'-'+name] != "")
            serializedObject.push({
              name: name,
              prefix: prefix,
              value: this.properties.get(prefix + '-' + name),
              type: 'literal'
            })
          }.bind(this)
        )

      return serializedObject
    },

    deserialize: function(serialize) {
      // Search in Serialize
      var initializedDocker = 0

      // Sort properties so that the hidden properties are first in the list
      serialize = serialize.sort(
        function(a, b) {
          a = Number(this.properties.keys().member(a.prefix + '-' + a.name))
          b = Number(this.properties.keys().member(b.prefix + '-' + b.name))
          return a > b ? 1 : a < b ? -1 : 0
        }.bind(this)
      )

      serialize.each(
        function(obj) {
          var name = obj.name
          var prefix = obj.prefix
          var value = obj.value

          // Complex properties can be real json objects, encode them to a string
          if (Object.prototype.toString.call(value) === 'Object') value = JSON.stringify(value)

          switch (prefix + '-' + name) {
            case 'raziel-parent':
              // Set parent
              if (!this.parent) {
                break
              }

              // Set outgoing Shape
              var parent = this.getCanvas().getChildShapeByResourceId(value)
              if (parent) {
                parent.add(this)
              }

              break
            default:
              // If list, eval as an array
              var prop = this.getStencil().property(prefix + '-' + name)
              if (prop && prop.isList() && typeof value === 'string') {
                if ((value || '').strip() && !value.startsWith('[') && !value.startsWith(']'))
                  value = '["' + value.strip() + '"]'
                value = ((value || '').strip() || '[]').evalJSON()
              }

              // Set property
              if (this.properties.keys().member(prefix + '-' + name)) {
                this.setProperty(prefix + '-' + name, value)
              } else if (
                !(
                  name === 'bounds' ||
                  name === 'parent' ||
                  name === 'target' ||
                  name === 'dockers' ||
                  name === 'docker' ||
                  name === 'outgoing' ||
                  name === 'incoming'
                )
              ) {
                this.setHiddenProperty(prefix + '-' + name, value)
              }
          }
        }.bind(this)
      )
    },

    toString: function() {
      return 'ORYX.Core.AbstractShape ' + this.id
    },

    /**
     * Converts the shape to a JSON representation.
     * @return {Object} A JSON object with included ORYX.Core.AbstractShape.JSONHelper and getShape() method.
     */
    toJSON: function() {
      //upgrade to prototype 1.6/1.7 breaks the jquery extend call. rebuilding the properties here.
      var mergedProperties = this.properties.merge(this.hiddenProperties)
      var resultProperties = new Hash()
      mergedProperties.each(
        function(pair) {
          var key = pair.key
          var value = pair.value

          //If complex property, value should be a json object
          if (
            this.getStencil().property(key) &&
            this.getStencil()
              .property(key)
              .type() === ORYX.CONFIG.TYPE_COMPLEX &&
            Object.prototype.toString.call(value) === 'String'
          ) {
            try {
              value = JSON.parse(value)
            } catch (error) {}

            // Parse date
          } else if (value instanceof Date && this.getStencil().property(key)) {
            try {
              value = value.format(
                this.getStencil()
                  .property(key)
                  .dateFormat()
              )
            } catch (e) {}
          }

          // Takes "my_property" instead of "oryx-my_property" as key
          key = key.replace(/^[\w_]+-/, '')
          resultProperties.set(key, value)
        }.bind(this)
      )

      var json = {
        resourceId: this.resourceId,
        properties: resultProperties.toObject(),
        stencil: {
          id: this.getStencil().idWithoutNs()
        },
        childShapes: this.getChildShapes().map(function(shape) {
          return shape.toJSON()
        })
      }

      if (this.getOutgoingShapes) {
        json.outgoing = this.getOutgoingShapes().map(function(shape) {
          return {
            resourceId: shape.resourceId
          }
        })
      }

      if (this.bounds) {
        json.bounds = {
          lowerRight: this.bounds.lowerRight(),
          upperLeft: this.bounds.upperLeft()
        }
      }

      if (this.dockers) {
        json.dockers = this.dockers.map(function(docker) {
          var d = docker.getDockedShape() && docker.referencePoint ? docker.referencePoint : docker.bounds.center()
          d.getDocker = function() {
            return docker
          }
          return d
        })
      }

      jQuery.extend(json, ORYX.Core.AbstractShape.JSONHelper)

      // do not pollute the json attributes (for serialization), so put the corresponding
      // shape is encapsulated in a method
      json.getShape = function() {
        return this
      }.bind(this)

      return json
    }
  }
)

/**
 * @namespace Collection of methods which can be used on a shape json object (ORYX.Core.AbstractShape#toJSON()).
 * @example
 * jQuery.extend(shapeAsJson, ORYX.Core.AbstractShape.JSONHelper);
 */
ORYX.Core.AbstractShape.JSONHelper = {
  /**
   * Iterates over each child shape.
   * @param {Object} iterator Iterator function getting a child shape and his parent as arguments.
   * @param {boolean} [deep=false] Iterate recursively (childShapes of childShapes)
   * @param {boolean} [modify=false] If true, the result of the iterator function is taken as new shape, return false to delete it. This enables modifying the object while iterating through the child shapes.
   * @example
   * // Increases the lowerRight x value of each direct child shape by one.
   * myShapeAsJson.eachChild(function(shape, parentShape){
   *     shape.bounds.lowerRight.x = shape.bounds.lowerRight.x + 1;
   *     return shape;
   * }, false, true);
   */
  eachChild: function(iterator, deep, modify) {
    if (!this.childShapes) return

    var newChildShapes = [] //needed if modify = true

    this.childShapes.each(
      function(shape) {
        if (!(shape.eachChild instanceof Function)) {
          jQuery.extend(shape, ORYX.Core.AbstractShape.JSONHelper)
        }
        var res = iterator(shape, this)
        if (res) newChildShapes.push(res) //if false is returned, and modify = true, current shape is deleted.

        if (deep) shape.eachChild(iterator, deep, modify)
      }.bind(this)
    )

    if (modify) this.childShapes = newChildShapes
  },

  getShape: function() {
    return null
  },
  getChildShapes: function(deep) {
    var allShapes = this.childShapes

    if (deep) {
      this.eachChild(function(shape) {
        if (!(shape.getChildShapes instanceof Function)) {
          jQuery.extend(shape, ORYX.Core.AbstractShape.JSONHelper)
        }
        allShapes = allShapes.concat(shape.getChildShapes(deep))
      }, true)
    }

    return allShapes
  },

  /**
   * @return {String} Serialized JSON object
   */
  serialize: function() {
    return JSON.stringify(this)
  }
}
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

/**
 * Init namespaces
 */
if (!ORYX) {
  var ORYX = {}
}

/**
   @namespace Namespace for the Oryx core elements.
   @name ORYX.Core
*/
if (!ORYX.Core) {
  ORYX.Core = {}
}

/**
 * @class Oryx canvas.
 * @extends ORYX.Core.AbstractShape
 *
 */
ORYX.Core.Canvas = ORYX.Core.AbstractShape.extend({
  /** @lends ORYX.Core.Canvas.prototype */

  /**
   * Defines the current zoom level
   */
  zoomLevel: 1,

  /**
   * Constructor
   */
  construct: function(options, stencil, facade) {
    arguments.callee.$.construct.apply(this, arguments)

    if (!(options && options.width && options.height)) {
      ORYX.Log.fatal('Canvas is missing mandatory parameters options.width and options.height.')
      return
    }
    this.facade = facade
    //TODO: set document resource id
    this.resourceId = options.id

    this.nodes = []

    this.edges = []

    // Row highlighting states
    this.colHighlightState = 0

    this.colHighlightEnabled = false

    //init svg document
    this.rootNode = ORYX.Editor.graft('http://www.w3.org/2000/svg', options.parentNode, [
      'svg',
      { id: this.id, width: options.width, height: options.height },
      ['defs', {}]
    ])

    this.rootNode.setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink')
    this.rootNode.setAttribute('xmlns:svg', 'http://www.w3.org/2000/svg')

    this._htmlContainer = ORYX.Editor.graft('http://www.w3.org/1999/xhtml', options.parentNode, [
      'div',
      { id: 'oryx_canvas_htmlContainer', style: 'position:absolute; top:5px' }
    ])

    // Additional SVG-node BELOW the stencils to allow underlays (if that is even a word) by plugins
    this.underlayNode = ORYX.Editor.graft('http://www.w3.org/2000/svg', this.rootNode, [
      'svg',
      { id: 'underlay-container' }
    ])

    // Create 2 svg-elements in the svg-container
    this.columnHightlight1 = ORYX.Editor.graft('http://www.w3.org/2000/svg', this.underlayNode, [
      'rect',
      { x: 0, width: ORYX.CONFIG.FORM_ROW_WIDTH + 35, height: '100%', style: 'fill: #fff6d5', visibility: 'hidden' }
    ])

    this.columnHightlight2 = ORYX.Editor.graft('http://www.w3.org/2000/svg', this.underlayNode, [
      'rect',
      {
        x: ORYX.CONFIG.FORM_ROW_WIDTH + 35,
        width: ORYX.CONFIG.FORM_ROW_WIDTH + 25,
        height: '100%',
        style: 'fill: #fff6d5',
        visibility: 'hidden'
      }
    ])

    this.node = ORYX.Editor.graft('http://www.w3.org/2000/svg', this.rootNode, [
      'g',
      {},
      ['g', { class: 'stencils' }, ['g', { class: 'me' }], ['g', { class: 'children' }], ['g', { class: 'edge' }]],
      ['g', { class: 'svgcontainer' }]
    ])

    /*
		var off = 2 * ORYX.CONFIG.GRID_DISTANCE;
		var size = 3;
		var d = "";
		for(var i = 0; i <= options.width; i += off)
			for(var j = 0; j <= options.height; j += off)
				d = d + "M" + (i - size) + " " + j + " l" + (2*size) + " 0 m" + (-size) + " " + (-size) + " l0 " + (2*size) + " m0" + (-size) + " ";
							
		ORYX.Editor.graft("http://www.w3.org/2000/svg", this.node.firstChild.firstChild,
			['path', {d:d , stroke:'#000000', 'stroke-width':'0.15px'},]);
		*/

    //Global definition of default font for shapes
    //Definitions in the SVG definition of a stencil will overwrite these settings for
    // that stencil.
    /*if(navigator.platform.indexOf("Mac") > -1) {
			this.node.setAttributeNS(null, 'stroke', 'black');
			this.node.setAttributeNS(null, 'stroke-width', '0.5px');
			this.node.setAttributeNS(null, 'font-family', 'Skia');
			//this.node.setAttributeNS(null, 'letter-spacing', '2px');
			this.node.setAttributeNS(null, 'font-size', ORYX.CONFIG.LABEL_DEFAULT_LINE_HEIGHT);
		} else {
			this.node.setAttributeNS(null, 'stroke', 'none');
			this.node.setAttributeNS(null, 'font-family', 'Verdana');
			this.node.setAttributeNS(null, 'font-size', ORYX.CONFIG.LABEL_DEFAULT_LINE_HEIGHT);
		}*/

    this.node.setAttributeNS(null, 'stroke', 'none')
    this.node.setAttributeNS(null, 'font-family', 'Verdana, sans-serif')
    this.node.setAttributeNS(null, 'font-size-adjust', 'none')
    this.node.setAttributeNS(null, 'font-style', 'normal')
    this.node.setAttributeNS(null, 'font-variant', 'normal')
    this.node.setAttributeNS(null, 'font-weight', 'normal')
    this.node.setAttributeNS(null, 'line-heigth', 'normal')

    this.node.setAttributeNS(null, 'font-size', ORYX.CONFIG.LABEL_DEFAULT_LINE_HEIGHT)

    this.bounds.set(0, 0, options.width, options.height)

    this.addEventHandlers(this.rootNode.parentNode)

    //disable context menu
    this.rootNode.oncontextmenu = function() {
      return false
    }
  },

  focus: function() {},

  setHightlightState: function(state) {
    if (this.colHighlightEnabled && this.colHighlightState != state) {
      if (state == 0) {
        this.columnHightlight1.setAttribute('visibility', 'hidden')
        this.columnHightlight2.setAttribute('visibility', 'hidden')
      } else if (state == 1) {
        this.columnHightlight1.setAttribute('visibility', 'visible')
        this.columnHightlight2.setAttribute('visibility', 'hidden')
      } else if (state == 2) {
        this.columnHightlight1.setAttribute('visibility', 'hidden')
        this.columnHightlight2.setAttribute('visibility', 'visible')
      } else if (state == 3) {
        this.columnHightlight1.setAttribute('visibility', 'visible')
        this.columnHightlight2.setAttribute('visibility', 'visible')
      }
      this.colHighlightState = state
    }
  },

  setHightlightStateBasedOnX: function(x) {
    if (x > ORYX.CONFIG.FORM_ROW_WIDTH + 30) {
      this.setHightlightState(2)
    } else {
      this.setHightlightState(1)
    }
  },

  update: function() {
    this.nodes.each(
      function(node) {
        this._traverseForUpdate(node)
      }.bind(this)
    )

    // call stencil's layout callback
    // (needed for row layouting of xforms)
    //this.getStencil().layout(this);

    var layoutEvents = this.getStencil().layout()

    if (layoutEvents) {
      layoutEvents.each(
        function(event) {
          // setup additional attributes
          event.shape = this
          event.forceExecution = true
          event.target = this.rootNode

          // do layouting

          this._delegateEvent(event)
        }.bind(this)
      )
    }

    this.nodes.invoke('_update')

    this.edges.invoke('_update', true)

    /*this.children.each(function(child) {
			child._update();
		});*/
  },

  _traverseForUpdate: function(shape) {
    var childRet = shape.isChanged
    shape.getChildNodes(
      false,
      function(child) {
        if (this._traverseForUpdate(child)) {
          childRet = true
        }
      }.bind(this)
    )

    if (childRet) {
      shape.layout()
      return true
    } else {
      return false
    }
  },

  layout: function() {},

  /**
   *
   * @param {Object} deep
   * @param {Object} iterator
   */
  getChildNodes: function(deep, iterator) {
    if (!deep && !iterator) {
      return this.nodes.clone()
    } else {
      var result = []
      this.nodes.each(function(uiObject) {
        if (iterator) {
          iterator(uiObject)
        }
        result.push(uiObject)

        if (deep && uiObject instanceof ORYX.Core.Shape) {
          result = result.concat(uiObject.getChildNodes(deep, iterator))
        }
      })

      return result
    }
  },

  /**
   * buggy crap! use base class impl instead!
   * @param {Object} iterator
   */
  /*	getChildEdges: function(iterator) {
		if(iterator) {
			this.edges.each(function(edge) {
				iterator(edge);
			});
		}
		
		return this.edges.clone();
	},
*/

  /**
   * Overrides the UIObject.add method. Adds uiObject to the correct sub node.
   * @param {UIObject} uiObject
   */
  add: function(uiObject, index, silent) {
    //if uiObject is child of another UIObject, remove it.
    if (uiObject instanceof ORYX.Core.UIObject) {
      if (!this.children.member(uiObject)) {
        //if uiObject is child of another parent, remove it from that parent.
        if (uiObject.parent) {
          uiObject.parent.remove(uiObject, true)
        }

        //add uiObject to the Canvas
        //add uiObject to this Shape
        if (index != undefined) this.children.splice(index, 0, uiObject)
        else this.children.push(uiObject)

        //set parent reference
        uiObject.parent = this

        //add uiObject.node to this.node depending on the type of uiObject
        if (uiObject instanceof ORYX.Core.Shape) {
          if (uiObject instanceof ORYX.Core.Edge) {
            uiObject.addMarkers(this.rootNode.getElementsByTagNameNS(NAMESPACE_SVG, 'defs')[0])
            uiObject.node = this.node.childNodes[0].childNodes[2].appendChild(uiObject.node)
            this.edges.push(uiObject)
          } else {
            uiObject.node = this.node.childNodes[0].childNodes[1].appendChild(uiObject.node)
            this.nodes.push(uiObject)
          }
        } else {
          //UIObject
          uiObject.node = this.node.appendChild(uiObject.node)
        }

        uiObject.bounds.registerCallback(this._changedCallback)

        if (this.eventHandlerCallback && silent !== true)
          this.eventHandlerCallback({ type: ORYX.CONFIG.EVENT_SHAPEADDED, shape: uiObject })
      } else {
        ORYX.Log.warn('add: ORYX.Core.UIObject is already a child of this object.')
      }
    } else {
      ORYX.Log.fatal('add: Parameter is not of type ORYX.Core.UIObject.')
    }
  },

  /**
   * Overrides the UIObject.remove method. Removes uiObject.
   * @param {UIObject} uiObject
   */
  remove: function(uiObject, silent) {
    //if uiObject is a child of this object, remove it.
    if (this.children.member(uiObject)) {
      //remove uiObject from children
      var parent = uiObject.parent

      this.children = this.children.without(uiObject)

      //delete parent reference of uiObject
      uiObject.parent = undefined

      //delete uiObject.node from this.node
      if (uiObject instanceof ORYX.Core.Shape) {
        if (uiObject instanceof ORYX.Core.Edge) {
          uiObject.removeMarkers()
          uiObject.node = this.node.childNodes[0].childNodes[2].removeChild(uiObject.node)
          this.edges = this.edges.without(uiObject)
        } else {
          uiObject.node = this.node.childNodes[0].childNodes[1].removeChild(uiObject.node)
          this.nodes = this.nodes.without(uiObject)
        }
      } else {
        //UIObject
        uiObject.node = this.node.removeChild(uiObject.node)
      }

      if (this.eventHandlerCallback && silent !== true)
        this.eventHandlerCallback({ type: ORYX.CONFIG.EVENT_SHAPEREMOVED, shape: uiObject, parent: parent })

      uiObject.bounds.unregisterCallback(this._changedCallback)
    } else {
      ORYX.Log.warn('remove: ORYX.Core.UIObject is not a child of this object.')
    }
  },

  removeAll: function() {
    var childShapes = this.getChildShapes()
    for (var i = 0; i < childShapes.length; i++) {
      var childObject = childShapes[i]
      this.remove(childObject)
    }
  },

  /**
     * Creates shapes out of the given collection of shape objects and adds them to the canvas.
     * @example 
     * canvas.addShapeObjects({
         bounds:{ lowerRight:{ y:510, x:633 }, upperLeft:{ y:146, x:210 } },
         resourceId:"oryx_F0715955-50F2-403D-9851-C08CFE70F8BD",
         childShapes:[],
         properties:{},
         stencil:{
           id:"Subprocess"
         },
         outgoing:[{resourceId: 'aShape'}],
         target: {resourceId: 'aShape'}
       });
     * @param {Object} shapeObjects 
     * @param {Function} [eventHandler] An event handler passed to each newly created shape (as eventHandlerCallback)
     * @return {Array} A collection of ORYX.Core.Shape
     * @methodOf ORYX.Core.Canvas.prototype
     */
  addShapeObjects: function(shapeObjects, eventHandler) {
    if (!shapeObjects) return

    this.initializingShapes = true

    /*FIXME This implementation is very evil! At first, all shapes are created on
          canvas. In a second step, the attributes are applied. There must be a distinction
          between the configuration phase (where the outgoings, for example, are just named),
          and the creation phase (where the outgoings are evaluated). This must be reflected
          in code to provide a nicer API/ implementation!!! */

    var addShape = function(shape, parent) {
      // Create a new Stencil
      var stencil = ORYX.Core.StencilSet.stencil(this.getStencil().namespace() + shape.stencil.id)

      // Create a new Shape
      var ShapeClass = stencil.type() == 'node' ? ORYX.Core.Node : ORYX.Core.Edge
      var newShape = new ShapeClass({ eventHandlerCallback: eventHandler }, stencil, this.facade)

      // Set the resource id
      newShape.resourceId = shape.resourceId
      newShape.node.id = 'svg-' + shape.resourceId

      // Set parent to json object to be used later
      // Due to the nested json structure, normally shape.parent is not set/ must not be set.
      // In special cases, it can be easier to set this directly instead of a nested structure.
      shape.parent = '#' + ((shape.parent && shape.parent.resourceId) || parent.resourceId)

      // Add the shape to the canvas
      this.add(newShape)

      return {
        json: shape,
        object: newShape
      }
    }.bind(this)

    /** Builds up recursively a flatted array of shapes, including a javascript object and json representation
     * @param {Object} shape Any object that has Object#childShapes
     */
    var addChildShapesRecursively = function(shape) {
      var addedShapes = []

      if (shape.childShapes && shape.childShapes.constructor == String) {
        shape.childShapes = JSON.parse(shape.childShapes)
      }

      shape.childShapes.each(function(childShape) {
        addedShapes.push(addShape(childShape, shape))
        addedShapes = addedShapes.concat(addChildShapesRecursively(childShape))
      })

      return addedShapes
    }.bind(this)

    var shapes = addChildShapesRecursively({
      childShapes: shapeObjects,
      resourceId: this.resourceId
    })

    // prepare deserialisation parameter
    shapes.each(
      function(shape) {
        var properties = []
        for (field in shape.json.properties) {
          properties.push({
            prefix: 'oryx',
            name: field,
            value: shape.json.properties[field]
          })
        }

        // Outgoings
        shape.json.outgoing.each(function(out) {
          properties.push({
            prefix: 'raziel',
            name: 'outgoing',
            value: '#' + out.resourceId
          })
        })

        // Target
        // (because of a bug, the first outgoing is taken when there is no target,
        // can be removed after some time)
        if (shape.object instanceof ORYX.Core.Edge) {
          var target = shape.json.target || shape.json.outgoing[0]
          if (target) {
            properties.push({
              prefix: 'raziel',
              name: 'target',
              value: '#' + target.resourceId
            })
          }
        }

        // Bounds
        if (shape.json.bounds) {
          properties.push({
            prefix: 'oryx',
            name: 'bounds',
            value:
              shape.json.bounds.upperLeft.x +
              ',' +
              shape.json.bounds.upperLeft.y +
              ',' +
              shape.json.bounds.lowerRight.x +
              ',' +
              shape.json.bounds.lowerRight.y
          })
        }

        //Dockers [{x:40, y:50}, {x:30, y:60}] => "40 50 30 60  #"
        if (shape.json.dockers) {
          properties.push({
            prefix: 'oryx',
            name: 'dockers',
            value:
              shape.json.dockers.inject('', function(dockersStr, docker) {
                return dockersStr + docker.x + ' ' + docker.y + ' '
              }) + ' #'
          })
        }

        //Parent
        properties.push({
          prefix: 'raziel',
          name: 'parent',
          value: shape.json.parent
        })

        shape.__properties = properties
      }.bind(this)
    )

    // Deserialize the properties from the shapes
    // This can't be done earlier because Shape#deserialize expects that all referenced nodes are already there

    // first, deserialize all nodes
    shapes.each(function(shape) {
      if (shape.object instanceof ORYX.Core.Node) {
        shape.object.deserialize(shape.__properties, shape.json)
      }
    })

    // second, deserialize all edges
    shapes.each(function(shape) {
      if (shape.object instanceof ORYX.Core.Edge) {
        shape.object.deserialize(shape.__properties, shape.json)
        shape.object._oldBounds = shape.object.bounds.clone()
        shape.object._update()
      }
    })

    delete this.initializingShapes
    return shapes.pluck('object')
  },

  /**
   * Updates the size of the canvas, regarding to the containg shapes.
   */
  updateSize: function() {
    // Check the size for the canvas
    var maxWidth = 0
    var maxHeight = 0
    var offset = 100
    this.getChildShapes(true, function(shape) {
      var b = shape.bounds
      maxWidth = Math.max(maxWidth, b.lowerRight().x + offset)
      maxHeight = Math.max(maxHeight, b.lowerRight().y + offset)
    })

    if (this.bounds.width() < maxWidth || this.bounds.height() < maxHeight) {
      this.setSize({
        width: Math.max(this.bounds.width(), maxWidth),
        height: Math.max(this.bounds.height(), maxHeight)
      })
    }
  },

  getRootNode: function() {
    return this.rootNode
  },

  getUnderlayNode: function() {
    return this.underlayNode
  },

  getSvgContainer: function() {
    return this.node.childNodes[1]
  },

  getHTMLContainer: function() {
    return this._htmlContainer
  },

  /**
   * Return all elements of the same highest level
   * @param {Object} elements
   */
  getShapesWithSharedParent: function(elements) {
    // If there is no elements, return []
    if (!elements || elements.length < 1) {
      return []
    }
    // If there is one element, return this element
    if (elements.length == 1) {
      return elements
    }

    return elements.findAll(function(value) {
      var parentShape = value.parent
      while (parentShape) {
        if (elements.member(parentShape)) return false
        parentShape = parentShape.parent
      }
      return true
    })
  },

  setSize: function(size, dontSetBounds) {
    if (!size || !size.width || !size.height) {
      return
    }

    if (this.rootNode.parentNode) {
      this.rootNode.parentNode.style.width = size.width + 'px'
      this.rootNode.parentNode.style.height = size.height + 'px'
    }

    this.rootNode.setAttributeNS(null, 'width', size.width)
    this.rootNode.setAttributeNS(null, 'height', size.height)

    //this._htmlContainer.style.top = "-" + (size.height + 4) + 'px';
    if (!dontSetBounds) {
      this.bounds.set({ a: { x: 0, y: 0 }, b: { x: size.width / this.zoomLevel, y: size.height / this.zoomLevel } })
    }
  },

  /**
   * Returns an SVG document of the current process.
   * @param {Boolean} escapeText Use true, if you want to parse it with an XmlParser,
   * 					false, if you want to use the SVG document in browser on client side.
   */
  getSVGRepresentation: function(escapeText) {
    // Get the serialized svg image source
    var svgClone = this.getRootNode().cloneNode(true)

    this._removeInvisibleElements(svgClone)

    var x1, y1, x2, y2
    this.getChildShapes(true).each(function(shape) {
      var absBounds = shape.absoluteBounds()
      var ul = absBounds.upperLeft()
      var lr = absBounds.lowerRight()
      if (x1 == undefined) {
        x1 = ul.x
        y1 = ul.y
        x2 = lr.x
        y2 = lr.y
      } else {
        x1 = Math.min(x1, ul.x)
        y1 = Math.min(y1, ul.y)
        x2 = Math.max(x2, lr.x)
        y2 = Math.max(y2, lr.y)
      }
    })

    var margin = 50
    var width, height, tx, ty
    if (x1 == undefined) {
      width = 0
      height = 0
      tx = 0
      ty = 0
    } else {
      width = x2
      height = y2
      tx = -x1 + margin / 2
      ty = -y1 + margin / 2
    }

    // Set the width and height
    svgClone.setAttributeNS(null, 'width', width + margin)
    svgClone.setAttributeNS(null, 'height', height + margin)

    //remove scale factor
    svgClone.childNodes[1].removeAttributeNS(null, 'transform')

    try {
      var svgCont = svgClone.childNodes[1].childNodes[1]
      svgCont.parentNode.removeChild(svgCont)
    } catch (e) {}

    if (escapeText) {
      $A(svgClone.getElementsByTagNameNS(ORYX.CONFIG.NAMESPACE_SVG, 'tspan')).each(function(elem) {
        elem.textContent = elem.textContent.escapeHTML()
      })

      $A(svgClone.getElementsByTagNameNS(ORYX.CONFIG.NAMESPACE_SVG, 'text')).each(function(elem) {
        if (elem.childNodes.length == 0) elem.textContent = elem.textContent.escapeHTML()
      })
    }

    // generating absolute urls for the pdf-exporter
    $A(svgClone.getElementsByTagNameNS(ORYX.CONFIG.NAMESPACE_SVG, 'image')).each(function(elem) {
      var href = elem.getAttributeNS('http://www.w3.org/1999/xlink', 'href')

      if (!href.match('^(http|https)://')) {
        href = window.location.protocol + '//' + window.location.host + href
        elem.setAttributeNS('http://www.w3.org/1999/xlink', 'href', href)
      }
    })

    // escape all links
    $A(svgClone.getElementsByTagNameNS(ORYX.CONFIG.NAMESPACE_SVG, 'a')).each(function(elem) {
      elem.setAttributeNS(
        'http://www.w3.org/1999/xlink',
        'xlink:href',
        (elem.getAttributeNS('http://www.w3.org/1999/xlink', 'href') || '').escapeHTML()
      )
    })

    return svgClone
  },

  /**
   * Removes all nodes (and its children) that has the
   * attribute visibility set to "hidden"
   */
  _removeInvisibleElements: function(element) {
    var index = 0
    while (index < element.childNodes.length) {
      var child = element.childNodes[index]
      if (child.getAttributeNS && child.getAttributeNS(null, 'visibility') === 'hidden') {
        element.removeChild(child)
      } else {
        this._removeInvisibleElements(child)
        index++
      }
    }
  },

  /**
   * This method checks all shapes on the canvas and removes all shapes that
   * contain invalid bounds values or dockers values(NaN)
   */
  /*cleanUp: function(parent) {
		if (!parent) {
			parent = this;
		}
		parent.getChildShapes().each(function(shape){
			var a = shape.bounds.a;
			var b = shape.bounds.b;
			if (isNaN(a.x) || isNaN(a.y) || isNaN(b.x) || isNaN(b.y)) {
				parent.remove(shape);
			}
			else {
				shape.getDockers().any(function(docker) {
					a = docker.bounds.a;
					b = docker.bounds.b;
					if (isNaN(a.x) || isNaN(a.y) || isNaN(b.x) || isNaN(b.y)) {
						parent.remove(shape);
						return true;
					}
					return false;
				});
				shape.getMagnets().any(function(magnet) {
					a = magnet.bounds.a;
					b = magnet.bounds.b;
					if (isNaN(a.x) || isNaN(a.y) || isNaN(b.x) || isNaN(b.y)) {
						parent.remove(shape);
						return true;
					}
					return false;
				});
				this.cleanUp(shape);
			}
		}.bind(this));
	},*/

  _delegateEvent: function(event) {
    if (this.eventHandlerCallback && (event.target == this.rootNode || event.target == this.rootNode.parentNode)) {
      this.eventHandlerCallback(event, this)
    }
  },

  toString: function() {
    return 'Canvas ' + this.id
  },

  /**
   * Calls {@link ORYX.Core.AbstractShape#toJSON} and adds some stencil set information.
   */
  toJSON: function() {
    var json = arguments.callee.$.toJSON.apply(this, arguments)

    //		if(ORYX.CONFIG.STENCILSET_HANDLER.length > 0) {
    //			json.stencilset = {
    //				url: this.getStencil().stencilSet().namespace()
    //	        };
    //		} else {
    json.stencilset = {
      url: this.getStencil()
        .stencilSet()
        .source(),
      namespace: this.getStencil()
        .stencilSet()
        .namespace()
    }
    //		}

    return json
  }
})
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

var idCounter = 0
var ID_PREFIX = 'resource'

/**
 * Main initialization method. To be called when loading
 * of the document, including all scripts, is completed.
 */
function init() {
  ORYX.Log.debug('Querying editor instances')

  // Hack for WebKit to set the SVGElement-Classes
  ORYX.Editor.setMissingClasses()

  // If someone wants to create the editor instance himself
  if (window.onOryxResourcesLoaded) {
    window.onOryxResourcesLoaded()
  }
  // Else fetch the model from server and display editor
  else {
    var modelId = window.location.search.substring(4)
    var modelUrl = './service/model/' + modelId + '/json'

    ORYX.Editor.createByUrl(modelUrl)
  }
}

/**
   @namespace Global Oryx name space
   @name ORYX
*/
if (!ORYX) {
  var ORYX = {}
}

/**
 * The Editor class.
 * @class ORYX.Editor
 * @extends Clazz
 * @param {Object} config An editor object, passed to {@link ORYX.Editor#loadSerialized}
 * @param {String} config.id Any ID that can be used inside the editor. If fullscreen=false, any HTML node with this id must be present to render the editor to this node.
 * @param {boolean} [config.fullscreen=true] Render editor in fullscreen mode or not.
 * @param {String} config.stencilset.url Stencil set URL.
 * @param {String} [config.stencil.id] Stencil type used for creating the canvas.
 * @param {Object} config.properties Any properties applied to the canvas.
 */
ORYX.Editor = {
  /** @lends ORYX.Editor.prototype */
  // Defines the global dom event listener
  DOMEventListeners: new Hash(),

  // Defines the selection
  selection: [],

  // Defines the current zoom level
  zoomLevel: 1.0,

  construct: function(config) {
    // initialization.
    this._eventsQueue = []
    this.loadedPlugins = []
    this.pluginsData = []

    //meta data about the model for the signavio warehouse
    //directory, new, name, description, revision, model (the model data)

    var model = config

    this.id = model.modelId

    if (config.model) {
      model = config.model
    }

    if (!this.id) {
      this.id = model.id
      if (!this.id) {
        this.id = ORYX.Editor.provideId()
      }
    }

    // Defines if the editor should be fullscreen or not
    this.fullscreen = config.fullscreen !== false

    // Initialize the eventlistener
    this._initEventListener()

    // CREATES the canvas
    this._createCanvas(model.stencil ? model.stencil.id : null, model.properties)

    // GENERATES the whole EXT.VIEWPORT
    this._generateGUI()

    // Initializing of a callback to check loading ends
    var loadPluginFinished = false
    var loadContentFinished = false
    var initFinished = function() {
      if (!loadPluginFinished || !loadContentFinished) {
        return
      }
      this._finishedLoading()
    }.bind(this)

    // LOAD the plugins
    window.setTimeout(
      function() {
        this.loadPlugins()
        loadPluginFinished = true
        initFinished()
      }.bind(this),
      100
    )

    // LOAD the content of the current editor instance
    window.setTimeout(
      function() {
        this.loadSerialized(model, true) // Request the meta data as well
        this.getCanvas().update()
        loadContentFinished = true
        initFinished()
        this.handleEvents({ type: ORYX.CONFIG.EVENT_EDITOR_INIT_COMPLETED })
      }.bind(this),
      200
    )
  },

  _finishedLoading: function() {
    // Raise Loaded Event
    this.handleEvents({ type: ORYX.CONFIG.EVENT_LOADED })
  },

  _initEventListener: function() {
    // Register on Events

    document.documentElement.addEventListener(ORYX.CONFIG.EVENT_KEYDOWN, this.catchKeyDownEvents.bind(this), false)
    document.documentElement.addEventListener(ORYX.CONFIG.EVENT_KEYUP, this.catchKeyUpEvents.bind(this), false)

    // Enable Key up and down Event
    this._keydownEnabled = true
    this._keyupEnabled = true

    this.DOMEventListeners.set(ORYX.CONFIG.EVENT_MOUSEDOWN, [])
    this.DOMEventListeners.set(ORYX.CONFIG.EVENT_MOUSEUP, [])
    this.DOMEventListeners.set(ORYX.CONFIG.EVENT_MOUSEOVER, [])
    this.DOMEventListeners.set(ORYX.CONFIG.EVENT_MOUSEOUT, [])
    this.DOMEventListeners.set(ORYX.CONFIG.EVENT_SELECTION_CHANGED, [])
    this.DOMEventListeners.set(ORYX.CONFIG.EVENT_MOUSEMOVE, [])
  },

  /**
   * Generate the whole viewport of the
   * Editor and initialized the Ext-Framework
   *
   */
  _generateGUI: function() {
    // Defines the layout height if it's NOT fullscreen
    var layoutHeight = ORYX.CONFIG.WINDOW_HEIGHT
    var canvasParent = this.getCanvas().rootNode.parentNode

    jQuery('#canvasSection').append(canvasParent)
    if (canvasParent.parentNode) {
      // Set the editor to the center, and refresh the size
      canvasParent.parentNode.setAttributeNS(null, 'align', 'center')
      canvasParent.setAttributeNS(null, 'align', 'left')
      this.getCanvas().setSize({
        width: ORYX.CONFIG.CANVAS_WIDTH,
        height: ORYX.CONFIG.CANVAS_HEIGHT
      })
    }
  },

  getAvailablePlugins: function() {
    var curAvailablePlugins = ORYX.availablePlugins.clone()
    curAvailablePlugins.each(
      function(plugin) {
        if (
          this.loadedPlugins.find(
            function(loadedPlugin) {
              return loadedPlugin.type == this.name
            }.bind(plugin)
          )
        ) {
          plugin.engaged = true
        } else {
          plugin.engaged = false
        }
      }.bind(this)
    )
    return curAvailablePlugins
  },

  loadScript: function(url, callback) {
    var script = document.createElement('script')
    script.type = 'text/javascript'
    if (script.readyState) {
      //IE
      script.onreadystatechange = function() {
        if (script.readyState == 'loaded' || script.readyState == 'complete') {
          script.onreadystatechange = null
          callback()
        }
      }
    } else {
      //Others
      script.onload = function() {
        callback()
      }
    }
    script.src = url
    document.getElementsByTagName('head')[0].appendChild(script)
  },
  /**
   * activate Plugin
   *
   * @param {String} name
   * @param {Function} callback
   * 		callback(sucess, [errorCode])
   * 			errorCodes: NOTUSEINSTENCILSET, REQUIRESTENCILSET, NOTFOUND, YETACTIVATED
   */
  activatePluginByName: function(name, callback, loadTry) {
    var match = this.getAvailablePlugins().find(function(value) {
      return value.name == name
    })
    if (match && (!match.engaged || match.engaged === 'false')) {
      var loadedStencilSetsNamespaces = this.getStencilSets().keys()
      var facade = this._getPluginFacade()
      var newPlugin
      var me = this
      ORYX.Log.debug("Initializing plugin '%0'", match.name)

      if (
        !match.requires ||
        !match.requires.namespaces ||
        match.requires.namespaces.any(function(req) {
          return loadedStencilSetsNamespaces.indexOf(req) >= 0
        })
      ) {
        if (
          !match.notUsesIn ||
          !match.notUsesIn.namespaces ||
          !match.notUsesIn.namespaces.any(function(req) {
            return loadedStencilSetsNamespaces.indexOf(req) >= 0
          })
        ) {
          try {
            var className = eval(match.name)
            var newPlugin = new className(facade, match)
            newPlugin.type = match.name

            // If there is an GUI-Plugin, they get all Plugins-Offer-Meta-Data
            if (newPlugin.registryChanged) newPlugin.registryChanged(me.pluginsData)

            // If there have an onSelection-Method it will pushed to the Editor Event-Handler
            if (newPlugin.onSelectionChanged)
              me.registerOnEvent(ORYX.CONFIG.EVENT_SELECTION_CHANGED, newPlugin.onSelectionChanged.bind(newPlugin))
            this.loadedPlugins.push(newPlugin)
            this.loadedPlugins.each(
              function(loaded) {
                if (loaded.registryChanged) loaded.registryChanged(this.pluginsData)
              }.bind(me)
            )
            callback(true)
          } catch (e) {
            ORYX.Log.warn('Plugin %0 is not available', match.name)
            if (!!loadTry) {
              callback(false, 'INITFAILED')
              return
            }
            this.loadScript(
              'plugins/scripts/' + match.source,
              this.activatePluginByName.bind(this, match.name, callback, true)
            )
          }
        } else {
          callback(false, 'NOTUSEINSTENCILSET')
          ORYX.Log.info("Plugin need a stencilset which is not loaded'", match.name)
        }
      } else {
        callback(false, 'REQUIRESTENCILSET')
        ORYX.Log.info("Plugin need a stencilset which is not loaded'", match.name)
      }
    } else {
      callback(false, match ? 'NOTFOUND' : 'YETACTIVATED')
      //TODO error handling
    }
  },

  /**
   *  Laden der Plugins
   */
  loadPlugins: function() {
    // if there should be plugins but still are none, try again.
    // TODO this should wait for every plugin respectively.
    /*if (!ORYX.Plugins && ORYX.availablePlugins.length > 0) {
			window.setTimeout(this.loadPlugins.bind(this), 100);
			return;
		}*/

    var me = this
    var newPlugins = []

    var loadedStencilSetsNamespaces = this.getStencilSets().keys()

    // Available Plugins will be initalize
    var facade = this._getPluginFacade()

    // If there is an Array where all plugins are described, than only take those
    // (that comes from the usage of oryx with a mashup api)
    if (ORYX.MashupAPI && ORYX.MashupAPI.loadablePlugins && ORYX.MashupAPI.loadablePlugins instanceof Array) {
      // Get the plugins from the available plugins (those who are in the plugins.xml)
      ORYX.availablePlugins = $A(ORYX.availablePlugins).findAll(function(value) {
        return ORYX.MashupAPI.loadablePlugins.include(value.name)
      })

      // Add those plugins to the list, which are only in the loadablePlugins list
      ORYX.MashupAPI.loadablePlugins.each(function(className) {
        if (
          !ORYX.availablePlugins.find(function(val) {
            return val.name == className
          })
        ) {
          ORYX.availablePlugins.push({ name: className })
        }
      })
    }

    ORYX.availablePlugins.each(function(value) {
      ORYX.Log.debug("Initializing plugin '%0'", value.get('name'))

      if (
        (!value.get('requires') ||
          !value.get('requires').namespaces ||
          value.get('requires').namespaces.any(function(req) {
            return loadedStencilSetsNamespaces.indexOf(req) >= 0
          })) &&
        (!value.get('notUsesIn') ||
          !value.get('notUsesIn').namespaces ||
          !value.get('notUsesIn').namespaces.any(function(req) {
            return loadedStencilSetsNamespaces.indexOf(req) >= 0
          })) &&
        /*only load activated plugins or undefined */
        (value.get('engaged') || value.get('engaged') === undefined)
      ) {
        try {
          var className = eval(value.get('name')) // wow funcky code here!
          if (className) {
            var plugin = new className(facade, value)
            plugin.type = value.get('name')
            newPlugins.push(plugin)
            plugin.engaged = true
          }
        } catch (e) {
          ORYX.Log.warn('Plugin %0 is not available %1', value.get('name'), e)
        }
      } else {
        ORYX.Log.info("Plugin need a stencilset which is not loaded'", value.get('name'))
      }
    })

    newPlugins.each(function(value) {
      // If there is an GUI-Plugin, they get all Plugins-Offer-Meta-Data
      if (value.registryChanged) value.registryChanged(me.pluginsData)

      // If there have an onSelection-Method it will pushed to the Editor Event-Handler
      if (value.onSelectionChanged)
        me.registerOnEvent(ORYX.CONFIG.EVENT_SELECTION_CHANGED, value.onSelectionChanged.bind(value))
    })

    this.loadedPlugins = newPlugins

    this.registerPluginsOnKeyEvents()

    this.setSelection()
  },

  /**
   * Creates the Canvas
   * @param {String} [stencilType] The stencil type used for creating the canvas. If not given, a stencil with myBeRoot = true from current stencil set is taken.
   * @param {Object} [canvasConfig] Any canvas properties (like language).
   */
  _createCanvas: function(stencilType, canvasConfig) {
    if (stencilType) {
      // Add namespace to stencilType
      if (stencilType.search(/^http/) === -1) {
        stencilType =
          this.getStencilSets()
            .values()[0]
            .namespace() + stencilType
      }
    } else {
      // Get any root stencil type
      stencilType = this.getStencilSets()
        .values()[0]
        .findRootStencilName()
    }

    // get the stencil associated with the type
    var canvasStencil = ORYX.Core.StencilSet.stencil(stencilType)

    if (!canvasStencil)
      ORYX.Log.fatal(
        'Initialisation failed, because the stencil with the type %0 is not part of one of the loaded stencil sets.',
        stencilType
      )

    // create all dom
    // TODO fix border, so the visible canvas has a double border and some spacing to the scrollbars
    var div = ORYX.Editor.graft('http://www.w3.org/1999/xhtml', null, ['div'])
    // set class for custom styling
    div.addClassName('ORYX_Editor')

    // create the canvas
    this._canvas = new ORYX.Core.Canvas(
      {
        width: ORYX.CONFIG.CANVAS_WIDTH,
        height: ORYX.CONFIG.CANVAS_HEIGHT,
        eventHandlerCallback: this.handleEvents.bind(this),
        id: this.id,
        parentNode: div
      },
      canvasStencil,
      this._getPluginFacade()
    )

    if (canvasConfig) {
      // Migrate canvasConfig to an RDF-like structure
      //FIXME this isn't nice at all because we don't want rdf any longer
      var properties = []
      for (field in canvasConfig) {
        properties.push({
          prefix: 'oryx',
          name: field,
          value: canvasConfig[field]
        })
      }

      this._canvas.deserialize(properties)
    }
  },

  /**
   * Returns a per-editor singleton plugin facade.
   * To be used in plugin initialization.
   */
  _getPluginFacade: function() {
    // if there is no pluginfacade already created:
    if (!this._pluginFacade)
      // create it.
      this._pluginFacade = {
        activatePluginByName: this.activatePluginByName.bind(this),
        //deactivatePluginByName:		this.deactivatePluginByName.bind(this),
        getAvailablePlugins: this.getAvailablePlugins.bind(this),
        offer: this.offer.bind(this),
        getStencilSets: this.getStencilSets.bind(this),
        getStencilSetExtensionDefinition: function() {
          return Object.clone(this.ss_extensions_def || {})
        }.bind(this),
        getRules: this.getRules.bind(this),
        loadStencilSet: this.loadStencilSet.bind(this),
        createShape: this.createShape.bind(this),
        deleteShape: this.deleteShape.bind(this),
        getSelection: this.getSelection.bind(this),
        setSelection: this.setSelection.bind(this),
        updateSelection: this.updateSelection.bind(this),
        getCanvas: this.getCanvas.bind(this),

        importJSON: this.importJSON.bind(this),
        getJSON: this.getJSON.bind(this),
        getSerializedJSON: this.getSerializedJSON.bind(this),

        executeCommands: this.executeCommands.bind(this),
        isExecutingCommands: this.isExecutingCommands.bind(this),

        registerOnEvent: this.registerOnEvent.bind(this),
        unregisterOnEvent: this.unregisterOnEvent.bind(this),
        raiseEvent: this.handleEvents.bind(this),
        enableEvent: this.enableEvent.bind(this),
        disableEvent: this.disableEvent.bind(this),

        eventCoordinates: this.eventCoordinates.bind(this),
        eventCoordinatesXY: this.eventCoordinatesXY.bind(this),

        getModelMetaData: this.getModelMetaData.bind(this)
      }

    // return it.
    return this._pluginFacade
  },

  isExecutingCommands: function() {
    return !!this.commandExecuting
  },

  /**
   * Implementes the command pattern
   * (The real usage of the command pattern
   * is implemented and shown in the Plugins/undo.js)
   *
   * @param <Oryx.Core.Command>[] Array of commands
   */
  executeCommands: function(commands) {
    if (!this.commandStack) {
      this.commandStack = []
    }
    if (!this.commandStackExecuted) {
      this.commandStackExecuted = []
    }

    this.commandStack = [].concat(this.commandStack).concat(commands)

    // Check if already executes
    if (this.commandExecuting) {
      return
    }

    // Start execution
    this.commandExecuting = true

    // Iterate over all commands
    while (this.commandStack.length > 0) {
      var command = this.commandStack.shift()
      // and execute it
      command.execute()
      this.commandStackExecuted.push(command)
    }

    // Raise event for executing commands
    this.handleEvents({
      type: ORYX.CONFIG.EVENT_EXECUTE_COMMANDS,
      commands: this.commandStackExecuted
    })

    // Remove temporary vars
    delete this.commandStack
    delete this.commandStackExecuted
    delete this.commandExecuting

    this.updateSelection()
  },

  /**
   * Returns JSON of underlying canvas (calls ORYX.Canvas#toJSON()).
   * @return {Object} Returns JSON representation as JSON object.
   */
  getJSON: function() {
    var canvasJSON = this.getCanvas().toJSON()
    canvasJSON.ssextensions = this.getStencilSets()
      .values()[0]
      .extensions()
      .keys()
      .findAll(function(sse) {
        return !sse.endsWith('/meta#')
      })
    return canvasJSON
  },

  /**
   * Serializes a call to toJSON().
   * @return {String} Returns JSON representation as string.
   */
  getSerializedJSON: function() {
    return JSON.stringify(this.getJSON())
  },

  /**
   * Imports shapes in JSON as expected by {@link ORYX.Editor#loadSerialized}
   * @param {Object|String} jsonObject The (serialized) json object to be imported
   * @param {boolean } [noSelectionAfterImport=false] Set to true if no shapes should be selected after import
   * @throws {SyntaxError} If the serialized json object contains syntax errors
   */
  importJSON: function(jsonObject, noSelectionAfterImport) {
    try {
      jsonObject = this.renewResourceIds(jsonObject)
    } catch (error) {
      throw error
    }
    //check, if the imported json model can be loaded in this editor
    // (stencil set has to fit)
    if (
      jsonObject.stencilset.namespace &&
      jsonObject.stencilset.namespace !==
        this.getCanvas()
          .getStencil()
          .stencilSet()
          .namespace()
    ) {
      alert(
        String.format(
          ORYX.I18N.JSONImport.wrongSS,
          jsonObject.stencilset.namespace,
          this.getCanvas()
            .getStencil()
            .stencilSet()
            .namespace()
        )
      )
      return null
    } else {
      var commandClass = ORYX.Core.Command.extend({
        construct: function(jsonObject, loadSerializedCB, noSelectionAfterImport, facade) {
          this.jsonObject = jsonObject
          this.noSelection = noSelectionAfterImport
          this.facade = facade
          this.shapes
          this.connections = []
          this.parents = new Hash()
          this.selection = this.facade.getSelection()
          this.loadSerialized = loadSerializedCB
        },
        execute: function() {
          if (!this.shapes) {
            // Import the shapes out of the serialization
            this.shapes = this.loadSerialized(this.jsonObject)

            //store all connections
            this.shapes.each(
              function(shape) {
                if (shape.getDockers) {
                  var dockers = shape.getDockers()
                  if (dockers) {
                    if (dockers.length > 0) {
                      this.connections.push([
                        dockers.first(),
                        dockers.first().getDockedShape(),
                        dockers.first().referencePoint
                      ])
                    }
                    if (dockers.length > 1) {
                      this.connections.push([
                        dockers.last(),
                        dockers.last().getDockedShape(),
                        dockers.last().referencePoint
                      ])
                    }
                  }
                }

                //store parents
                this.parents[shape.id] = shape.parent
              }.bind(this)
            )
          } else {
            this.shapes.each(
              function(shape) {
                this.parents[shape.id].add(shape)
              }.bind(this)
            )

            this.connections.each(function(con) {
              con[0].setDockedShape(con[1])
              con[0].setReferencePoint(con[2])
              con[0].update()
            })
          }

          //this.parents.values().uniq().invoke("update");
          this.facade.getCanvas().update()

          if (!this.noSelection) this.facade.setSelection(this.shapes)
          else this.facade.updateSelection()

          // call updateSize again, because during loadSerialized the edges' bounds
          // are not yet initialized properly
          this.facade.getCanvas().updateSize()
        },
        rollback: function() {
          var selection = this.facade.getSelection()

          this.shapes.each(
            function(shape) {
              selection = selection.without(shape)
              this.facade.deleteShape(shape)
            }.bind(this)
          )

          /*this.parents.values().uniq().each(function(parent) {
						if(!this.shapes.member(parent))
							parent.update();
					}.bind(this));*/

          this.facade.getCanvas().update()

          this.facade.setSelection(selection)
        }
      })

      var command = new commandClass(
        jsonObject,
        this.loadSerialized.bind(this),
        noSelectionAfterImport,
        this._getPluginFacade()
      )

      this.executeCommands([command])

      return command.shapes.clone()
    }
  },

  /**
   * This method renew all resource Ids and according references.
   * Warning: The implementation performs a substitution on the serialized object for
   * easier implementation. This results in a low performance which is acceptable if this
   * is only used when importing models.
   * @param {Object|String} jsonObject
   * @throws {SyntaxError} If the serialized json object contains syntax errors.
   * @return {Object} The jsonObject with renewed ids.
   * @private
   */
  renewResourceIds: function(jsonObject) {
    // For renewing resource ids, a serialized and object version is needed
    if (Object.prototype.toString.call(jsonObject) === 'String') {
      try {
        var serJsonObject = jsonObject
        jsonObject = JSON.parse(jsonObject)
      } catch (error) {
        throw new SyntaxError(error.message)
      }
    } else {
      var serJsonObject = JSON.stringify(jsonObject)
    }

    // collect all resourceIds recursively
    var collectResourceIds = function(shapes) {
      if (!shapes) return []

      return shapes
        .map(function(shape) {
          return collectResourceIds(shape.childShapes).concat(shape.resourceId)
        })
        .flatten()
    }
    var resourceIds = collectResourceIds(jsonObject.childShapes)

    // Replace each resource id by a new one
    resourceIds.each(function(oldResourceId) {
      var newResourceId = ORYX.Editor.provideId()
      serJsonObject = serJsonObject.replace(new RegExp(oldResourceId, 'g'), newResourceId)
    })

    return JSON.parse(serJsonObject)
  },

  /**
   * Loads serialized model to the oryx.
   * @example
   * editor.loadSerialized({
   *    resourceId: "mymodel1",
   *    childShapes: [
   *       {
   *          stencil:{ id:"Subprocess" },
   *          outgoing:[{resourceId: 'aShape'}],
   *          target: {resourceId: 'aShape'},
   *          bounds:{ lowerRight:{ y:510, x:633 }, upperLeft:{ y:146, x:210 } },
   *          resourceId: "myshape1",
   *          childShapes:[],
   *          properties:{},
   *       }
   *    ],
   *    properties:{
   *       language: "English"
   *    },
   *    stencilset:{
   *       url:"http://localhost:8080/oryx/stencilsets/bpmn1.1/bpmn1.1.json"
   *    },
   *    stencil:{
   *       id:"BPMNDiagram"
   *    }
   * });
   * @param {Object} model Description of the model to load.
   * @param {Array} [model.ssextensions] List of stenctil set extensions.
   * @param {String} model.stencilset.url
   * @param {String} model.stencil.id
   * @param {Array} model.childShapes
   * @param {Array} [model.properties]
   * @param {String} model.resourceId
   * @return {ORYX.Core.Shape[]} List of created shapes
   * @methodOf ORYX.Editor.prototype
   */
  loadSerialized: function(model, requestMeta) {
    var canvas = this.getCanvas()

    // Bugfix (cf. http://code.google.com/p/oryx-editor/issues/detail?id=240)
    // Deserialize the canvas' stencil set extensions properties first!
    this.loadSSExtensions(model.ssextensions)

    // Load Meta Data Extension if available
    // #Signavio
    if (requestMeta === true) {
      var metaDataExtension = this.getExtensionForMetaData()
      if (metaDataExtension) {
        this.loadSSExtension(metaDataExtension)
      }
    }

    var shapes = this.getCanvas().addShapeObjects(model.childShapes, this.handleEvents.bind(this))

    if (model.properties) {
      for (key in model.properties) {
        var value = model.properties[key]
        var prop = this.getCanvas()
          .getStencil()
          .property('oryx-' + key)
        if (!(typeof value === 'string') && (!prop || !prop.isList())) {
          value = JSON.stringify(value)
        }
        this.getCanvas().setProperty('oryx-' + key, value)
      }
    }

    this.getCanvas().updateSize()

    // Force to update the selection
    this.selection = [null]
    this.setSelection([])

    return shapes
  },

  /**
   * Return the namespace of the extension which
   * provided all the self defined meta data
   * @return {String} Returns null if no extension is defined, otherwise the namespace
   *
   */
  getExtensionForMetaData: function() {
    if (!this.ss_extensions_def || !(this.ss_extensions_def.extensions instanceof Array)) {
      return null
    }

    var stencilsets = this.getStencilSets()
    var extension = this.ss_extensions_def.extensions.find(function(ex) {
      return !!stencilsets[ex['extends']] && ex.namespace.endsWith('/meta#')
    })

    return extension ? extension.namespace || null : null
  },

  /**
   * Calls ORYX.Editor.prototype.ss_extension_namespace for each element
   * @param {Array} ss_extension_namespaces An array of stencil set extension namespaces.
   */
  loadSSExtensions: function(ss_extension_namespaces) {
    if (!ss_extension_namespaces) return

    ss_extension_namespaces.each(
      function(ss_extension_namespace) {
        this.loadSSExtension(ss_extension_namespace)
      }.bind(this)
    )
  },

  /**
   * Loads a stencil set extension.
   * The stencil set extensions definiton file must already
   * be loaded when the editor is initialized.
   */
  loadSSExtension: function(ss_extension_namespace) {
    if (this.ss_extensions_def) {
      var extension = this.ss_extensions_def.extensions.find(function(ex) {
        return ex.namespace == ss_extension_namespace
      })

      if (!extension) {
        return
      }

      var stencilset = this.getStencilSets()[extension['extends']]

      if (!stencilset) {
        return
      }

      // Check if absolute or relative url
      if ((extension['definition'] || '').startsWith('/')) {
        stencilset.addExtension(extension['definition'])
      } else {
        stencilset.addExtension(ORYX.CONFIG.SS_EXTENSIONS_FOLDER + extension['definition'])
      }

      //stencilset.addExtension("/oryx/build/stencilsets/extensions/" + extension["definition"])
      this.getRules().initializeRules(stencilset)

      this._getPluginFacade().raiseEvent({
        type: ORYX.CONFIG.EVENT_STENCIL_SET_LOADED
      })
    }
  },

  disableEvent: function(eventType) {
    if (eventType == ORYX.CONFIG.EVENT_KEYDOWN) {
      this._keydownEnabled = false
    }
    if (eventType == ORYX.CONFIG.EVENT_KEYUP) {
      this._keyupEnabled = false
    }
    if (this.DOMEventListeners.keys().member(eventType)) {
      var value = this.DOMEventListeners.unset(eventType)
      this.DOMEventListeners.set('disable_' + eventType, value)
    }
  },

  enableEvent: function(eventType) {
    if (eventType == ORYX.CONFIG.EVENT_KEYDOWN) {
      this._keydownEnabled = true
    }

    if (eventType == ORYX.CONFIG.EVENT_KEYUP) {
      this._keyupEnabled = true
    }

    if (this.DOMEventListeners.keys().member('disable_' + eventType)) {
      var value = this.DOMEventListeners.unset('disable_' + eventType)
      this.DOMEventListeners.set(eventType, value)
    }
  },

  /**
   *  Methods for the PluginFacade
   */
  registerOnEvent: function(eventType, callback) {
    if (!this.DOMEventListeners.keys().member(eventType)) {
      this.DOMEventListeners.set(eventType, [])
    }

    this.DOMEventListeners.get(eventType).push(callback)
  },

  unregisterOnEvent: function(eventType, callback) {
    if (this.DOMEventListeners.keys().member(eventType)) {
      this.DOMEventListeners.set(eventType, this.DOMEventListeners.get(eventType).without(callback))
    } else {
      // Event is not supported
      // TODO: Error Handling
    }
  },

  getSelection: function() {
    return this.selection || []
  },

  getStencilSets: function() {
    return ORYX.Core.StencilSet.stencilSets(this.id)
  },

  getRules: function() {
    return ORYX.Core.StencilSet.rules(this.id)
  },

  loadStencilSet: function(source) {
    try {
      ORYX.Core.StencilSet.loadStencilSet(source, this.modelMetaData, this.id)
      this.handleEvents({ type: ORYX.CONFIG.EVENT_STENCIL_SET_LOADED })
    } catch (e) {
      ORYX.Log.warn('Requesting stencil set file failed. (' + e + ')')
    }
  },

  offer: function(pluginData) {
    if (!this.pluginsData.member(pluginData)) {
      this.pluginsData.push(pluginData)
    }
  },

  /**
   * It creates an new event or adds the callback, if already existing,
   * for the key combination that the plugin passes in keyCodes attribute
   * of the offer method.
   *
   * The new key down event fits the schema:
   * 		key.event[.metactrl][.alt][.shift].'thekeyCode'
   */
  registerPluginsOnKeyEvents: function() {
    this.pluginsData.each(
      function(pluginData) {
        if (pluginData.keyCodes) {
          pluginData.keyCodes.each(
            function(keyComb) {
              var eventName = 'key.event'

              /* Include key action */
              eventName += '.' + keyComb.keyAction

              if (keyComb.metaKeys) {
                /* Register on ctrl or apple meta key as meta key */
                if (keyComb.metaKeys.indexOf(ORYX.CONFIG.META_KEY_META_CTRL) > -1) {
                  eventName += '.' + ORYX.CONFIG.META_KEY_META_CTRL
                }

                /* Register on alt key as meta key */
                if (keyComb.metaKeys.indexOf(ORYX.CONFIG.META_KEY_ALT) > -1) {
                  eventName += '.' + ORYX.CONFIG.META_KEY_ALT
                }

                /* Register on shift key as meta key */
                if (keyComb.metaKeys.indexOf(ORYX.CONFIG.META_KEY_SHIFT) > -1) {
                  eventName += '.' + ORYX.CONFIG.META_KEY_SHIFT
                }
              }

              /* Register on the actual key */
              if (keyComb.keyCode) {
                eventName += '.' + keyComb.keyCode
              }

              /* Register the event */
              ORYX.Log.debug('Register Plugin on Key Event: %0', eventName)
              if (pluginData.toggle === true && pluginData.buttonInstance) {
                this.registerOnEvent(eventName, function() {
                  pluginData.buttonInstance.toggle(!pluginData.buttonInstance.pressed) // Toggle
                  pluginData.functionality.call(
                    pluginData,
                    pluginData.buttonInstance,
                    pluginData.buttonInstance.pressed
                  ) // Call function
                })
              } else {
                this.registerOnEvent(eventName, pluginData.functionality)
              }
            }.bind(this)
          )
        }
      }.bind(this)
    )
  },

  isEqual: function(a, b) {
    return (
      a === b ||
      (a.length === b.length &&
        a.all(function(r) {
          return b.include(r)
        }))
    )
  },

  isDirty: function(a) {
    return a.any(function(shape) {
      return shape.isPropertyChanged()
    })
  },

  setSelection: function(elements, subSelectionElement, force) {
    if (!elements) {
      elements = []
    }
    if (!(elements instanceof Array)) {
      elements = [elements]
    }

    elements = elements.findAll(function(n) {
      return n && n instanceof ORYX.Core.Shape
    })

    if (elements[0] instanceof ORYX.Core.Canvas) {
      elements = []
    }

    if (!force && this.isEqual(this.selection, elements) && !this.isDirty(elements)) {
      return
    }

    this.selection = elements
    this._subSelection = subSelectionElement

    this.handleEvents({
      type: ORYX.CONFIG.EVENT_SELECTION_CHANGED,
      elements: elements,
      subSelection: subSelectionElement,
      force: !!force
    })
  },

  updateSelection: function() {
    this.setSelection(this.selection, this._subSelection, true)
    /*var s = this.selection;
		this.setSelection();
		this.setSelection(s);*/
  },

  getCanvas: function() {
    return this._canvas
  },

  /**
   *	option = {
   *		type: string,
   *		position: {x:int, y:int},
   *		connectingType:	uiObj-Class
   *		connectedShape: uiObj
   *		draggin: bool
   *		namespace: url
   *       parent: ORYX.Core.AbstractShape
   *		template: a template shape that the newly created inherits properties from.
   *		}
   */
  createShape: function(option) {
    if (option && option.serialize && option.serialize instanceof Array) {
      var type = option.serialize.find(function(obj) {
        return obj.prefix + '-' + obj.name == 'oryx-type'
      })
      var stencil = ORYX.Core.StencilSet.stencil(type.value)

      if (stencil.type() == 'node') {
        var newShapeObject = new ORYX.Core.Node(
          { eventHandlerCallback: this.handleEvents.bind(this) },
          stencil,
          this._getPluginFacade()
        )
      } else {
        var newShapeObject = new ORYX.Core.Edge(
          { eventHandlerCallback: this.handleEvents.bind(this) },
          stencil,
          this._getPluginFacade()
        )
      }

      this.getCanvas().add(newShapeObject)
      newShapeObject.deserialize(option.serialize)

      return newShapeObject
    }

    // If there is no argument, throw an exception
    if (!option || !option.type || !option.namespace) {
      throw 'To create a new shape you have to give an argument with type and namespace'
    }

    var canvas = this.getCanvas()
    var newShapeObject

    // Get the shape type
    var shapetype = option.type

    // Get the stencil set
    var sset = ORYX.Core.StencilSet.stencilSet(option.namespace)
    // Create an New Shape, dependents on an Edge or a Node
    if (sset.stencil(shapetype).type() == 'node') {
      newShapeObject = new ORYX.Core.Node(
        { eventHandlerCallback: this.handleEvents.bind(this) },
        sset.stencil(shapetype),
        this._getPluginFacade()
      )
    } else {
      newShapeObject = new ORYX.Core.Edge(
        { eventHandlerCallback: this.handleEvents.bind(this) },
        sset.stencil(shapetype),
        this._getPluginFacade()
      )
    }

    // when there is a template, inherit the properties.
    if (option.template) {
      newShapeObject._jsonStencil.properties = option.template._jsonStencil.properties
      newShapeObject.postProcessProperties()
    }

    // Add to the canvas
    if (option.parent && newShapeObject instanceof ORYX.Core.Node) {
      option.parent.add(newShapeObject)
    } else {
      canvas.add(newShapeObject)
    }

    // Set the position
    var point = option.position ? option.position : { x: 100, y: 200 }

    var con
    // If there is create a shape and in the argument there is given an ConnectingType and is instance of an edge
    if (option.connectingType && option.connectedShape && !(newShapeObject instanceof ORYX.Core.Edge)) {
      // there will be create a new Edge
      con = new ORYX.Core.Edge(
        { eventHandlerCallback: this.handleEvents.bind(this) },
        sset.stencil(option.connectingType)
      )

      // And both endings dockers will be referenced to the both shapes
      con.dockers.first().setDockedShape(option.connectedShape)

      var magnet = option.connectedShape.getDefaultMagnet()
      var cPoint = magnet ? magnet.bounds.center() : option.connectedShape.bounds.midPoint()
      con.dockers.first().setReferencePoint(cPoint)
      con.dockers.last().setDockedShape(newShapeObject)
      con.dockers.last().setReferencePoint(newShapeObject.getDefaultMagnet().bounds.center())

      // The Edge will be added to the canvas and be updated
      canvas.add(con)
      //con.update();
    }

    // Move the new Shape to the position
    if (newShapeObject instanceof ORYX.Core.Edge && option.connectedShape) {
      newShapeObject.dockers.first().setDockedShape(option.connectedShape)

      if (option.connectedShape instanceof ORYX.Core.Node) {
        newShapeObject.dockers.first().setReferencePoint(option.connectedShape.getDefaultMagnet().bounds.center())
        newShapeObject.dockers.last().bounds.centerMoveTo(point)
      } else {
        newShapeObject.dockers.first().setReferencePoint(option.connectedShape.bounds.midPoint())
      }

      var start = newShapeObject.dockers.first()
      var end = newShapeObject.dockers.last()

      if (start.getDockedShape() && end.getDockedShape()) {
        var startPoint = start.getAbsoluteReferencePoint()
        var endPoint = end.getAbsoluteReferencePoint()

        var docker = newShapeObject.createDocker()
        docker.bounds.centerMoveTo({
          x: startPoint.x + (endPont.x - startPoint.x) / 2,
          y: startPoint.y + (endPont.y - startPoint.y) / 2
        })
      }
    } else {
      var b = newShapeObject.bounds
      if (newShapeObject instanceof ORYX.Core.Node && newShapeObject.dockers.length == 1) {
        b = newShapeObject.dockers.first().bounds
      }

      b.centerMoveTo(point)

      var upL = b.upperLeft()
      b.moveBy(-Math.min(upL.x, 0), -Math.min(upL.y, 0))

      var lwR = b.lowerRight()
      b.moveBy(-Math.max(lwR.x - canvas.bounds.width(), 0), -Math.max(lwR.y - canvas.bounds.height(), 0))
    }

    // Update the shape
    if (newShapeObject instanceof ORYX.Core.Edge) {
      newShapeObject._update(false)
    }

    // And refresh the selection
    if (!(newShapeObject instanceof ORYX.Core.Edge) && !option.dontUpdateSelection) {
      newShapeObject._name = option._name
      this.setSelection([newShapeObject])
    }

    if (con && con.alignDockers) {
      //con.alignDockers();
    }
    if (newShapeObject.alignDockers) {
      newShapeObject.alignDockers()
    }

    return newShapeObject
  },

  deleteShape: function(shape) {
    if (!shape || !shape.parent) {
      return
    }

    //remove shape from parent
    // this also removes it from DOM
    shape.parent.remove(shape)

    //delete references to outgoing edges
    shape.getOutgoingShapes().each(function(os) {
      var docker = os.getDockers().first()
      if (docker && docker.getDockedShape() == shape) {
        docker.setDockedShape(undefined)
      }
    })

    //delete references to incoming edges
    shape.getIncomingShapes().each(function(is) {
      var docker = is.getDockers().last()
      if (docker && docker.getDockedShape() == shape) {
        docker.setDockedShape(undefined)
      }
    })

    //delete references of the shape's dockers
    shape.getDockers().each(function(docker) {
      docker.setDockedShape(undefined)
    })
  },

  /**
   * Returns an object with meta data about the model.
   * Like name, description, ...
   *
   * Empty object with the current backend.
   *
   * @return {Object} Meta data about the model
   */
  getModelMetaData: function() {
    return this.modelMetaData
  },

  /* Event-Handler Methods */

  /**
   * Helper method to execute an event immediately. The event is not
   * scheduled in the _eventsQueue. Needed to handle Layout-Callbacks.
   */
  _executeEventImmediately: function(eventObj) {
    if (this.DOMEventListeners.keys().member(eventObj.event.type)) {
      this.DOMEventListeners.get(eventObj.event.type).each(
        function(value) {
          value(eventObj.event, eventObj.arg)
        }.bind(this)
      )
    }
  },

  _executeEvents: function() {
    this._queueRunning = true
    while (this._eventsQueue.length > 0) {
      var val = this._eventsQueue.shift()
      this._executeEventImmediately(val)
    }
    this._queueRunning = false
  },

  /**
   * Leitet die Events an die Editor-Spezifischen Event-Methoden weiter
   * @param {Object} event Event , welches gefeuert wurde
   * @param {Object} uiObj Target-UiObj
   */
  handleEvents: function(event, uiObj) {
    ORYX.Log.trace('Dispatching event type %0 on %1', event.type, uiObj)

    switch (event.type) {
      case ORYX.CONFIG.EVENT_MOUSEDOWN:
        this._handleMouseDown(event, uiObj)
        break
      case ORYX.CONFIG.EVENT_MOUSEMOVE:
        this._handleMouseMove(event, uiObj)
        break
      case ORYX.CONFIG.EVENT_MOUSEUP:
        this._handleMouseUp(event, uiObj)
        break
      case ORYX.CONFIG.EVENT_MOUSEOVER:
        this._handleMouseHover(event, uiObj)
        break
      case ORYX.CONFIG.EVENT_MOUSEOUT:
        this._handleMouseOut(event, uiObj)
        break
    }
    /* Force execution if necessary. Used while handle Layout-Callbacks. */
    if (event.forceExecution) {
      this._executeEventImmediately({ event: event, arg: uiObj })
    } else {
      this._eventsQueue.push({ event: event, arg: uiObj })
    }

    if (!this._queueRunning) {
      this._executeEvents()
    }

    // TODO: Make this return whether no listener returned false.
    // So that, when one considers bubbling undesireable, it won't happen.
    return false
  },

  isValidEvent: function(e) {
    try {
      var isInput = ['INPUT', 'TEXTAREA'].include(e.target.tagName.toUpperCase())
      var gridHasFocus =
        e.target.className.include('x-grid3-focus') && !e.target.className.include('x-grid3-focus-canvas')
      return !isInput && !gridHasFocus
    } catch (e) {
      return false
    }
  },

  catchKeyUpEvents: function(event) {
    if (!this._keyupEnabled) {
      return
    }
    /* assure we have the current event. */
    if (!event) event = window.event

    // Checks if the event comes from some input field
    if (!this.isValidEvent(event)) {
      return
    }

    /* Create key up event type */
    var keyUpEvent = this.createKeyCombEvent(event, ORYX.CONFIG.KEY_ACTION_UP)

    ORYX.Log.debug('Key Event to handle: %0', keyUpEvent)

    /* forward to dispatching. */
    this.handleEvents({ type: keyUpEvent, event: event })
  },

  /**
   * Catches all key down events and forward the appropriated event to
   * dispatching concerning to the pressed keys.
   *
   * @param {Event}
   * 		The key down event to handle
   */
  catchKeyDownEvents: function(event) {
    if (!this._keydownEnabled) {
      return
    }
    /* Assure we have the current event. */
    if (!event) event = window.event

    /* Fixed in FF3 */
    // This is a mac-specific fix. The mozilla event object has no knowledge
    // of meta key modifier on osx, however, it is needed for certain
    // shortcuts. This fix adds the metaKey field to the event object, so
    // that all listeners that registered per Oryx plugin facade profit from
    // this. The original bug is filed in
    // https://bugzilla.mozilla.org/show_bug.cgi?id=418334
    //if (this.__currentKey == ORYX.CONFIG.KEY_CODE_META) {
    //	event.appleMetaKey = true;
    //}
    //this.__currentKey = pressedKey;

    // Checks if the event comes from some input field
    if (!this.isValidEvent(event)) {
      return
    }

    /* Create key up event type */
    var keyDownEvent = this.createKeyCombEvent(event, ORYX.CONFIG.KEY_ACTION_DOWN)

    ORYX.Log.debug('Key Event to handle: %0', keyDownEvent)

    /* Forward to dispatching. */
    this.handleEvents({ type: keyDownEvent, event: event })
  },

  /**
   * Creates the event type name concerning to the pressed keys.
   *
   * @param {Event} keyDownEvent
   * 		The source keyDownEvent to build up the event name
   */
  createKeyCombEvent: function(keyEvent, keyAction) {
    /* Get the currently pressed key code. */
    var pressedKey = keyEvent.which || keyEvent.keyCode
    //this.__currentKey = pressedKey;

    /* Event name */
    var eventName = 'key.event'

    /* Key action */
    if (keyAction) {
      eventName += '.' + keyAction
    }

    /* Ctrl or apple meta key is pressed */
    if (keyEvent.ctrlKey || keyEvent.metaKey) {
      eventName += '.' + ORYX.CONFIG.META_KEY_META_CTRL
    }

    /* Alt key is pressed */
    if (keyEvent.altKey) {
      eventName += '.' + ORYX.CONFIG.META_KEY_ALT
    }

    /* Alt key is pressed */
    if (keyEvent.shiftKey) {
      eventName += '.' + ORYX.CONFIG.META_KEY_SHIFT
    }

    /* Return the composed event name */
    return eventName + '.' + pressedKey
  },

  _handleMouseDown: function(event, uiObj) {
    // get canvas.
    var canvas = this.getCanvas()
    // Try to get the focus
    canvas.focus()

    // find the shape that is responsible for this element's id.
    var element = event.currentTarget
    var elementController = uiObj

    // gather information on selection.
    var currentIsSelectable =
      elementController !== null && elementController !== undefined && elementController.isSelectable
    var currentIsMovable = elementController !== null && elementController !== undefined && elementController.isMovable
    var modifierKeyPressed = event.shiftKey || event.ctrlKey
    var noObjectsSelected = this.selection.length === 0
    var currentIsSelected = this.selection.member(elementController)

    // Rule #1: When there is nothing selected, select the clicked object.
    if (currentIsSelectable && noObjectsSelected) {
      this.setSelection([elementController])

      ORYX.Log.trace('Rule #1 applied for mouse down on %0', element.id)

      // Rule #3: When at least one element is selected, and there is no
      // control key pressed, and the clicked object is not selected, select
      // the clicked object.
    } else if (currentIsSelectable && !noObjectsSelected && !modifierKeyPressed && !currentIsSelected) {
      this.setSelection([elementController])

      //var objectType = elementController.readAttributes();
      //alert(objectType[0] + ": " + objectType[1]);

      ORYX.Log.trace('Rule #3 applied for mouse down on %0', element.id)

      // Rule #4: When the control key is pressed, and the current object is
      // not selected, add it to the selection.
    } else if (currentIsSelectable && modifierKeyPressed && !currentIsSelected) {
      var newSelection = this.selection.clone()
      newSelection.push(elementController)
      this.setSelection(newSelection)

      ORYX.Log.trace('Rule #4 applied for mouse down on %0', element.id)

      // Rule #6
    } else if (currentIsSelectable && currentIsSelected && modifierKeyPressed) {
      var newSelection = this.selection.clone()
      this.setSelection(newSelection.without(elementController))

      ORYX.Log.trace('Rule #6 applied for mouse down on %0', elementController.id)

      // Rule #5: When there is at least one object selected and no control
      // key pressed, we're dragging.
      /*} else if(currentIsSelectable && !noObjectsSelected
			&& !modifierKeyPressed) {

			if(this.log.isTraceEnabled())
				this.log.trace("Rule #5 applied for mouse down on "+element.id);
*/
      // Rule #2: When clicked on something that is neither
      // selectable nor movable, clear the selection, and return.
    } else if (!currentIsSelectable && !currentIsMovable) {
      this.setSelection([])

      ORYX.Log.trace('Rule #2 applied for mouse down on %0', element.id)

      return

      // Rule #7: When the current object is not selectable but movable,
      // it is probably a control. Leave the selection unchanged but set
      // the movedObject to the current one and enable Drag. Dockers will
      // be processed in the dragDocker plugin.
    } else if (!currentIsSelectable && currentIsMovable && !(elementController instanceof ORYX.Core.Controls.Docker)) {
      // TODO: If there is any moveable elements, do this in a plugin
      //ORYX.Core.UIEnableDrag(event, elementController);

      ORYX.Log.trace('Rule #7 applied for mouse down on %0', element.id)

      // Rule #8: When the element is selectable and is currently selected and no
      // modifier key is pressed
    } else if (currentIsSelectable && currentIsSelected && !modifierKeyPressed) {
      this._subSelection = this._subSelection != elementController ? elementController : undefined

      this.setSelection(this.selection, this._subSelection)

      ORYX.Log.trace('Rule #8 applied for mouse down on %0', element.id)
    }

    // prevent event from bubbling, return.
    //Event.stop(event);
    return
  },

  _handleMouseMove: function(event, uiObj) {
    return
  },

  _handleMouseUp: function(event, uiObj) {
    // get canvas.
    var canvas = this.getCanvas()

    // find the shape that is responsible for this elemement's id.
    var elementController = uiObj

    //get event position
    var evPos = this.eventCoordinates(event)

    //Event.stop(event);
  },

  _handleMouseHover: function(event, uiObj) {
    return
  },

  _handleMouseOut: function(event, uiObj) {
    return
  },

  /**
   * Calculates the event coordinates to SVG document coordinates.
   * @param {Event} event
   * @return {SVGPoint} The event coordinates in the SVG document
   */
  eventCoordinates: function(event) {
    var canvas = this.getCanvas()

    var svgPoint = canvas.node.ownerSVGElement.createSVGPoint()
    svgPoint.x = event.clientX
    svgPoint.y = event.clientY

    var additionalIEZoom = 1
    if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) {
      var ua = navigator.userAgent
      if (ua.indexOf('MSIE') >= 0) {
        //IE 10 and below
        var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100)
        if (zoom !== 100) {
          additionalIEZoom = zoom / 100
        }
      }
    }

    if (additionalIEZoom !== 1) {
      svgPoint.x = svgPoint.x * additionalIEZoom
      svgPoint.y = svgPoint.y * additionalIEZoom
    }

    var matrix = canvas.node.getScreenCTM()
    return svgPoint.matrixTransform(matrix.inverse())
  },

  eventCoordinatesXY: function(x, y) {
    var canvas = this.getCanvas()

    var svgPoint = canvas.node.ownerSVGElement.createSVGPoint()
    svgPoint.x = x
    svgPoint.y = y

    var additionalIEZoom = 1
    if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) {
      var ua = navigator.userAgent
      if (ua.indexOf('MSIE') >= 0) {
        //IE 10 and below
        var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100)
        if (zoom !== 100) {
          additionalIEZoom = zoom / 100
        }
      }
    }

    if (additionalIEZoom !== 1) {
      svgPoint.x = svgPoint.x * additionalIEZoom
      svgPoint.y = svgPoint.y * additionalIEZoom
    }

    var matrix = canvas.node.getScreenCTM()
    return svgPoint.matrixTransform(matrix.inverse())
  }
}
ORYX.Editor = Clazz.extend(ORYX.Editor)

/**
 * Creates a new ORYX.Editor instance by fetching a model from given url and passing it to the constructur
 * @param {String} modelUrl The JSON URL of a model.
 * @param {Object} config Editor config passed to the constructur, merged with the response of the request to modelUrl
 */
ORYX.Editor.createByUrl = function(modelUrl) {
  new Ajax.Request(modelUrl, {
    method: 'GET',
    onSuccess: function(transport) {
      var editorConfig = JSON.parse(transport.responseText)
      new ORYX.Editor(editorConfig)
    }.bind(this)
  })
}

// TODO Implement namespace awareness on attribute level.
/**
 * graft() function
 * Originally by Sean M. Burke from interglacial.com, altered for usage with
 * SVG and namespace (xmlns) support. Be sure you understand xmlns before
 * using this funtion, as it creates all grafted elements in the xmlns
 * provided by you and all element's attribures in default xmlns. If you
 * need to graft elements in a certain xmlns and wish to assign attributes
 * in both that and another xmlns, you will need to do stepwise grafting,
 * adding non-default attributes yourself or you'll have to enhance this
 * function. Latter, I would appreciate: martin�apfelfabrik.de
 * @param {Object} namespace The namespace in which
 * 					elements should be grafted.
 * @param {Object} parent The element that should contain the grafted
 * 					structure after the function returned.
 * @param {Object} t the crafting structure.
 * @param {Object} doc the document in which grafting is performed.
 */
ORYX.Editor.graft = function(namespace, parent, t, doc) {
  doc = doc || (parent && parent.ownerDocument) || document
  var e
  if (t === undefined) {
    throw "Can't graft an undefined value"
  } else if (t.constructor == String) {
    e = doc.createTextNode(t)
  } else {
    for (var i = 0; i < t.length; i++) {
      if (i === 0 && t[i].constructor == String) {
        var snared
        snared = t[i].match(/^([a-z][a-z0-9]*)\.([^\s\.]+)$/i)
        if (snared) {
          e = doc.createElementNS(namespace, snared[1])
          e.setAttributeNS(null, 'class', snared[2])
          continue
        }
        snared = t[i].match(/^([a-z][a-z0-9]*)$/i)
        if (snared) {
          e = doc.createElementNS(namespace, snared[1]) // but no class
          continue
        }

        // Otherwise:
        e = doc.createElementNS(namespace, 'span')
        e.setAttribute(null, 'class', 'namelessFromLOL')
      }

      if (t[i] === undefined) {
        throw "Can't graft an undefined value in a list!"
      } else if (t[i].constructor == String || t[i].constructor == Array) {
        this.graft(namespace, e, t[i], doc)
      } else if (t[i].constructor == Number) {
        this.graft(namespace, e, t[i].toString(), doc)
      } else if (t[i].constructor == Object) {
        // hash's properties => element's attributes
        for (var k in t[i]) {
          e.setAttributeNS(null, k, t[i][k])
        }
      } else {
      }
    }
  }
  if (parent && parent.appendChild) {
    parent.appendChild(e)
  } else {
  }
  return e // return the topmost created node
}

ORYX.Editor.provideId = function() {
  var res = [],
    hex = '0123456789ABCDEF'

  for (var i = 0; i < 36; i++) res[i] = Math.floor(Math.random() * 0x10)

  res[14] = 4
  res[19] = (res[19] & 0x3) | 0x8

  for (var i = 0; i < 36; i++) res[i] = hex[res[i]]

  res[8] = res[13] = res[18] = res[23] = '-'

  return 'oryx_' + res.join('')
}

/**
 * When working with Ext, conditionally the window needs to be resized. To do
 * so, use this class method. Resize is deferred until 100ms, and all subsequent
 * resizeBugFix calls are ignored until the initially requested resize is
 * performed.
 */
ORYX.Editor.resizeFix = function() {
  if (!ORYX.Editor._resizeFixTimeout) {
    ORYX.Editor._resizeFixTimeout = window.setTimeout(function() {
      window.resizeBy(1, 1)
      window.resizeBy(-1, -1)
      ORYX.Editor._resizefixTimeout = null
    }, 100)
  }
}

ORYX.Editor.Cookie = {
  callbacks: [],

  onChange: function(callback, interval) {
    this.callbacks.push(callback)
    this.start(interval)
  },

  start: function(interval) {
    if (this.pe) {
      return
    }

    var currentString = document.cookie

    this.pe = new PeriodicalExecuter(
      function() {
        if (currentString != document.cookie) {
          currentString = document.cookie
          this.callbacks.each(
            function(callback) {
              callback(this.getParams())
            }.bind(this)
          )
        }
      }.bind(this),
      (interval || 10000) / 1000
    )
  },

  stop: function() {
    if (this.pe) {
      this.pe.stop()
      this.pe = null
    }
  },

  getParams: function() {
    var res = {}

    var p = document.cookie
    p.split('; ').each(function(param) {
      res[param.split('=')[0]] = param.split('=')[1]
    })

    return res
  },

  toString: function() {
    return document.cookie
  }
}

/**
 * Workaround for SAFARI/Webkit, because
 * when trying to check SVGSVGElement of instanceof there is
 * raising an error
 *
 */
ORYX.Editor.SVGClassElementsAreAvailable = true
ORYX.Editor.setMissingClasses = function() {
  try {
    SVGElement
  } catch (e) {
    ORYX.Editor.SVGClassElementsAreAvailable = false
    SVGSVGElement = document.createElementNS('http://www.w3.org/2000/svg', 'svg').toString()
    SVGGElement = document.createElementNS('http://www.w3.org/2000/svg', 'g').toString()
    SVGPathElement = document.createElementNS('http://www.w3.org/2000/svg', 'path').toString()
    SVGTextElement = document.createElementNS('http://www.w3.org/2000/svg', 'text').toString()
    //SVGMarkerElement 	= document.createElementNS('http://www.w3.org/2000/svg', 'marker').toString();
    SVGRectElement = document.createElementNS('http://www.w3.org/2000/svg', 'rect').toString()
    SVGImageElement = document.createElementNS('http://www.w3.org/2000/svg', 'image').toString()
    SVGCircleElement = document.createElementNS('http://www.w3.org/2000/svg', 'circle').toString()
    SVGEllipseElement = document.createElementNS('http://www.w3.org/2000/svg', 'ellipse').toString()
    SVGLineElement = document.createElementNS('http://www.w3.org/2000/svg', 'line').toString()
    SVGPolylineElement = document.createElementNS('http://www.w3.org/2000/svg', 'polyline').toString()
    SVGPolygonElement = document.createElementNS('http://www.w3.org/2000/svg', 'polygon').toString()
  }
}
ORYX.Editor.checkClassType = function(classInst, classType) {
  if (ORYX.Editor.SVGClassElementsAreAvailable) {
    return classInst instanceof classType
  } else {
    return classInst == classType
  }
}
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

/**
 * Init namespaces
 */
if (!ORYX) {
  var ORYX = {}
}
if (!ORYX.Core) {
  ORYX.Core = {}
}

new (function() {
  ORYX.Core.UIEnableDrag = function(event, uiObj, option) {
    this.uiObj = uiObj
    var upL = uiObj.bounds.upperLeft()

    var a = uiObj.node.getScreenCTM()
    this.faktorXY = { x: a.a, y: a.d }

    this.scrollNode = uiObj.node.ownerSVGElement.parentNode.parentNode

    this.offSetPosition = {
      x: Event.pointerX(event) - upL.x * this.faktorXY.x,
      y: Event.pointerY(event) - upL.y * this.faktorXY.y
    }

    this.offsetScroll = { x: this.scrollNode.scrollLeft, y: this.scrollNode.scrollTop }

    this.dragCallback = ORYX.Core.UIDragCallback.bind(this)
    this.disableCallback = ORYX.Core.UIDisableDrag.bind(this)

    this.movedCallback = option ? option.movedCallback : undefined
    this.upCallback = option ? option.upCallback : undefined

    document.documentElement.addEventListener(ORYX.CONFIG.EVENT_MOUSEUP, this.disableCallback, true)
    document.documentElement.addEventListener(ORYX.CONFIG.EVENT_MOUSEMOVE, this.dragCallback, false)
  }

  ORYX.Core.UIDragCallback = function(event) {
    var position = {
      x: Event.pointerX(event) - this.offSetPosition.x,
      y: Event.pointerY(event) - this.offSetPosition.y
    }

    position.x -= this.offsetScroll.x - this.scrollNode.scrollLeft
    position.y -= this.offsetScroll.y - this.scrollNode.scrollTop

    position.x /= this.faktorXY.x
    position.y /= this.faktorXY.y

    this.uiObj.bounds.moveTo(position)
    //this.uiObj.update();

    if (this.movedCallback) this.movedCallback(event)

    //Event.stop(event);
  }

  ORYX.Core.UIDisableDrag = function(event) {
    document.documentElement.removeEventListener(ORYX.CONFIG.EVENT_MOUSEMOVE, this.dragCallback, false)
    document.documentElement.removeEventListener(ORYX.CONFIG.EVENT_MOUSEUP, this.disableCallback, true)

    if (this.upCallback) this.upCallback(event)

    this.upCallback = undefined
    this.movedCallback = undefined

    Event.stop(event)
  }

  /**
   * Implements a command to move docker by an offset.
   *
   * @class ORYX.Core.MoveDockersCommand
   * @param {Object} object An object with the docker id as key and docker and offset as object value
   *
   */

  ORYX.Core.MoveDockersCommand = ORYX.Core.Command.extend({
    construct: function(dockers) {
      this.dockers = new Hash(dockers)
      this.edges = new Hash()
    },
    execute: function() {
      if (this.changes) {
        this.executeAgain()
        return
      } else {
        this.changes = new Hash()
      }

      this.dockers.values().each(
        function(docker) {
          var edge = docker.docker.parent
          if (!edge) {
            return
          }

          if (!this.changes.get(edge.getId())) {
            this.changes.set(edge.getId(), {
              edge: edge,
              oldDockerPositions: edge.dockers.map(function(r) {
                return r.bounds.center()
              })
            })
          }
          docker.docker.bounds.moveBy(docker.offset)
          this.edges.set(edge.getId(), edge)
          docker.docker.update()
        }.bind(this)
      )
      this.edges.each(
        function(edge) {
          this.updateEdge(edge.value)
          if (this.changes[edge.value.getId()])
            this.changes[edge.value.getId()].dockerPositions = edge.value.dockers.map(function(r) {
              return r.bounds.center()
            })
        }.bind(this)
      )
    },
    updateEdge: function(edge) {
      edge._update(true)
      ;[edge.getOutgoingShapes(), edge.getIncomingShapes()].flatten().invoke('_update', [true])
    },
    executeAgain: function() {
      this.changes.values().each(
        function(change) {
          // Reset the dockers
          this.removeAllDocker(change.edge)
          change.dockerPositions.each(
            function(pos, i) {
              if (i == 0 || i == change.dockerPositions.length - 1) {
                return
              }
              var docker = change.edge.createDocker(undefined, pos)
              docker.bounds.centerMoveTo(pos)
              docker.update()
            }.bind(this)
          )
          this.updateEdge(change.edge)
        }.bind(this)
      )
    },
    rollback: function() {
      this.changes.values().each(
        function(change) {
          // Reset the dockers
          this.removeAllDocker(change.edge)
          change.oldDockerPositions.each(
            function(pos, i) {
              if (i == 0 || i == change.oldDockerPositions.length - 1) {
                return
              }
              var docker = change.edge.createDocker(undefined, pos)
              docker.bounds.centerMoveTo(pos)
              docker.update()
            }.bind(this)
          )
          this.updateEdge(change.edge)
        }.bind(this)
      )
    },
    removeAllDocker: function(edge) {
      edge.dockers.slice(1, edge.dockers.length - 1).each(function(docker) {
        edge.removeDocker(docker)
      })
    }
  })
})()
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

/**
 * Init namespaces
 */
if (!ORYX) {
  var ORYX = {}
}
if (!ORYX.Core) {
  ORYX.Core = {}
}

/**
 * @classDescription Base class for Shapes.
 * @extends ORYX.Core.AbstractShape
 */
ORYX.Core.Shape = {
  /**
   * Constructor
   */
  construct: function(options, stencil, facade) {
    // call base class constructor
    arguments.callee.$.construct.apply(this, arguments)

    this.facade = facade
    this.dockers = []
    this.magnets = []

    this._defaultMagnet

    this.incoming = []
    this.outgoing = []

    this.nodes = []

    this._dockerChangedCallback = this._dockerChanged.bind(this)

    //Hash map for all labels. Labels are not treated as children of shapes.
    this._labels = new Hash()

    // create SVG node
    this.node = ORYX.Editor.graft('http://www.w3.org/2000/svg', null, [
      'g',
      { id: 'svg-' + this.resourceId },
      [
        'g',
        { class: 'stencils' },
        ['g', { class: 'me' }],
        ['g', { class: 'children', style: 'overflow:hidden' }],
        ['g', { class: 'edge' }]
      ],
      ['g', { class: 'controls' }, ['g', { class: 'dockers' }], ['g', { class: 'magnets' }]]
    ])
  },

  /**
   * If changed flag is set, refresh method is called.
   */
  update: function() {
    //if(this.isChanged) {
    //this.layout();
    //}
  },

  /**
   * !!!Not called from any sub class!!!
   */
  _update: function() {},

  /**
   * Calls the super class refresh method
   *  and updates the svg elements that are referenced by a property.
   */
  refresh: function() {
    //call base class refresh method
    arguments.callee.$.refresh.apply(this, arguments)

    if (this.node.ownerDocument) {
      //adjust SVG to properties' values
      var me = this
      this.propertiesChanged.each(
        function(propChanged) {
          if (propChanged.value) {
            var prop = this.properties.get(propChanged.key)
            var property = this.getStencil().property(propChanged.key)
            if (property != undefined) {
              this.propertiesChanged.set(propChanged.key, false)

              //handle choice properties
              if (property.type() == ORYX.CONFIG.TYPE_CHOICE) {
                //iterate all references to SVG elements
                property.refToView().each(
                  function(ref) {
                    //if property is referencing a label, update the label
                    if (ref !== '') {
                      var label = this._labels.get(this.id + ref)
                      if (label && property.item(prop)) {
                        label.text(property.item(prop).title())
                      }
                    }
                  }.bind(this)
                )

                //if the choice's items are referencing SVG elements
                // show the selected and hide all other referenced SVG
                // elements
                var refreshedSvgElements = new Hash()
                property.items().each(
                  function(item) {
                    item.refToView().each(
                      function(itemRef) {
                        if (itemRef == '') {
                          return
                        }

                        var svgElem = this.node.ownerDocument.getElementById(this.id + itemRef)

                        if (!svgElem) {
                          return
                        }

                        /* Do not refresh the same svg element multiple times */
                        if (!refreshedSvgElements.get(svgElem.id) || prop == item.value()) {
                          svgElem.setAttributeNS(null, 'display', prop == item.value() ? 'inherit' : 'none')
                          refreshedSvgElements.set(svgElem.id, svgElem)
                        }

                        // Reload the href if there is an image-tag
                        if (ORYX.Editor.checkClassType(svgElem, SVGImageElement)) {
                          svgElem.setAttributeNS(
                            'http://www.w3.org/1999/xlink',
                            'href',
                            svgElem.getAttributeNS('http://www.w3.org/1999/xlink', 'href')
                          )
                        }
                      }.bind(this)
                    )
                  }.bind(this)
                )
              } else {
                //handle properties that are not of type choice
                //iterate all references to SVG elements
                property.refToView().each(
                  function(ref) {
                    //if the property does not reference an SVG element,
                    // do nothing
                    if (ref === '') {
                      return
                    }

                    var refId = this.id + ref

                    if (property.type() === ORYX.CONFIG.TYPE_FLOWABLE_MULTIINSTANCE) {
                      if (ref === 'multiinstance') {
                        var svgElemParallel = this.node.ownerDocument.getElementById(this.id + 'parallel')
                        if (svgElemParallel) {
                          if (prop === 'Parallel') {
                            svgElemParallel.setAttributeNS(null, 'display', 'inherit')
                          } else {
                            svgElemParallel.setAttributeNS(null, 'display', 'none')
                          }
                        }

                        var svgElemSequential = this.node.ownerDocument.getElementById(this.id + 'sequential')

                        if (svgElemSequential) {
                          if (prop === 'Sequential') {
                            svgElemSequential.setAttributeNS(null, 'display', 'inherit')
                          } else {
                            svgElemSequential.setAttributeNS(null, 'display', 'none')
                          }
                        }
                      }
                      return
                    } else if (property.type() === 'cancelactivity') {
                      var svgElemFrame = this.node.ownerDocument.getElementById(this.id + 'frame')
                      var svgElemFrame2 = this.node.ownerDocument.getElementById(this.id + 'frame2')

                      if (prop === 'true') {
                        svgElemFrame.setAttributeNS(null, 'display', 'inherit')
                        svgElemFrame2.setAttributeNS(null, 'display', 'inherit')
                      } else {
                        svgElemFrame.setAttributeNS(null, 'display', 'none')
                        svgElemFrame2.setAttributeNS(null, 'display', 'none')
                      }
                    }

                    //get the SVG element
                    var svgElem = this.node.ownerDocument.getElementById(refId)

                    //if the SVG element can not be found
                    if (!svgElem || !svgElem.ownerSVGElement) {
                      //if the referenced SVG element is a SVGAElement, it cannot
                      // be found with getElementById (Firefox bug).
                      // this is a work around
                      if (
                        property.type() === ORYX.CONFIG.TYPE_URL ||
                        property.type() === ORYX.CONFIG.TYPE_DIAGRAM_LINK
                      ) {
                        var svgElems = this.node.ownerDocument.getElementsByTagNameNS('http://www.w3.org/2000/svg', 'a')

                        svgElem = $A(svgElems).find(function(elem) {
                          return elem.getAttributeNS(null, 'id') === refId
                        })

                        if (!svgElem) {
                          return
                        }
                      } else {
                        //this.propertiesChanged[propChanged.key] = true;
                        return
                      }
                    }

                    if (property.complexAttributeToView()) {
                      var label = this._labels.get(refId)
                      if (label) {
                        try {
                          propJson = prop.evalJSON()
                          var value = propJson[property.complexAttributeToView()]
                          label.text(value ? value : prop)
                        } catch (e) {
                          label.text(prop)
                        }
                      }
                    } else {
                      switch (property.type()) {
                        case ORYX.CONFIG.TYPE_BOOLEAN:
                          if (typeof prop == 'string') prop = prop === 'true'

                          svgElem.setAttributeNS(
                            null,
                            'display',
                            !(prop === property.inverseBoolean()) ? 'inherit' : 'none'
                          )

                          break
                        case ORYX.CONFIG.TYPE_COLOR:
                          if (property.fill()) {
                            if (svgElem.tagName.toLowerCase() === 'stop') {
                              if (prop) {
                                if (property.lightness() && property.lightness() !== 1) {
                                  prop = ORYX.Utils.adjustLightness(prop, property.lightness())
                                }

                                svgElem.setAttributeNS(null, 'stop-color', prop)

                                // Adjust stop color of the others
                                if (svgElem.parentNode.tagName.toLowerCase() === 'radialgradient') {
                                  ORYX.Utils.adjustGradient(svgElem.parentNode, svgElem)
                                }
                              }

                              // If there is no value, set opaque
                              if (svgElem.parentNode.tagName.toLowerCase() === 'radialgradient') {
                                $A(svgElem.parentNode.getElementsByTagName('stop')).each(
                                  function(stop) {
                                    stop.setAttributeNS(
                                      null,
                                      'stop-opacity',
                                      prop
                                        ? stop.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'default-stop-opacity') || 1
                                        : 0
                                    )
                                  }.bind(this)
                                )
                              }
                            } else {
                              svgElem.setAttributeNS(null, 'fill', prop)
                            }
                          }
                          if (property.stroke()) {
                            svgElem.setAttributeNS(null, 'stroke', prop)
                          }
                          break
                        case ORYX.CONFIG.TYPE_STRING:
                          var label = this._labels.get(refId)
                          if (label) {
                            label.text(prop)
                          }
                          break
                        case ORYX.CONFIG.TYPE_EXPRESSION:
                          var label = this._labels.get(refId)
                          if (label) {
                            label.text(prop)
                          }
                          break
                        case ORYX.CONFIG.TYPE_DATASOURCE:
                          var label = this._labels.get(refId)
                          if (label) {
                            label.text(prop)
                          }
                          break
                        case ORYX.CONFIG.TYPE_INTEGER:
                          var label = this._labels.get(refId)
                          if (label) {
                            label.text(prop)
                          }
                          break
                        case ORYX.CONFIG.TYPE_FLOAT:
                          if (property.fillOpacity()) {
                            svgElem.setAttributeNS(null, 'fill-opacity', prop)
                          }
                          if (property.strokeOpacity()) {
                            svgElem.setAttributeNS(null, 'stroke-opacity', prop)
                          }
                          if (!property.fillOpacity() && !property.strokeOpacity()) {
                            var label = this._labels.get(refId)
                            if (label) {
                              label.text(prop)
                            }
                          }
                          break

                        case ORYX.CONFIG.TYPE_SUB_PROCESS_LINK:
                          if (ref == 'subprocesslink') {
                            var onclickAttr = svgElem.getAttributeNodeNS('', 'onclick')
                            var styleAttr = svgElem.getAttributeNodeNS('', 'style')

                            if (onclickAttr) {
                              if (prop && prop.id) {
                                if (styleAttr) {
                                  styleAttr.textContent = 'cursor:pointer;'
                                }
                                onclickAttr.textContent =
                                  'FLOWABLE.TOOLBAR.ACTIONS.navigateToProcess(' + prop.id + ');return false;'
                              } else {
                                if (styleAttr) {
                                  styleAttr.textContent = 'cursor:default;'
                                }
                                onclickAttr.textContent = 'return false;'
                              }
                            }
                          }
                          break

                        case ORYX.CONFIG.TYPE_URL:
                          break
                      }
                    }
                  }.bind(this)
                )
              }
            }
          }
        }.bind(this)
      )

      //update labels
      this._labels.values().each(function(label) {
        label.update()
      })
    }
  },

  layout: function() {
    //this.getStencil().layout(this)
    var layoutEvents = this.getStencil().layout()
    if (layoutEvents) {
      layoutEvents.each(
        function(event) {
          // setup additional attributes
          event.shape = this
          event.forceExecution = true

          // do layouting
          this._delegateEvent(event)
        }.bind(this)
      )
    }
  },

  /**
   * Returns an array of Label objects.
   */
  getLabels: function() {
    return this._labels.values()
  },

  /**
   * Returns the label for a given ref
   * @return {ORYX.Core.Label} Returns null if there is no label
   */
  getLabel: function(ref) {
    if (!ref) {
      return null
    }
    return (
      (
        this._labels.find(function(o) {
          return o.key.endsWith(ref)
        }) || {}
      ).value || null
    )
  },

  /**
   * Hides all related labels
   *
   */
  hideLabels: function() {
    this.getLabels().invoke('hide')
  },

  /**
   * Shows all related labels
   *
   */
  showLabels: function() {
    var labels = this.getLabels()
    labels.invoke('show')
    labels.each(function(label) {
      label.update()
    })
  },

  setOpacity: function(value, animate) {
    value = Math.max(Math.min(typeof value == 'number' ? value : 1.0, 1.0), 0.0)

    if (value !== 1.0) {
      value = String(value)
      this.node.setAttributeNS(null, 'fill-opacity', value)
      this.node.setAttributeNS(null, 'stroke-opacity', value)
    } else {
      this.node.removeAttributeNS(null, 'fill-opacity')
      this.node.removeAttributeNS(null, 'stroke-opacity')
    }
  },

  /**
   * Returns an array of dockers of this object.
   */
  getDockers: function() {
    return this.dockers
  },

  getMagnets: function() {
    return this.magnets
  },

  getDefaultMagnet: function() {
    if (this._defaultMagnet) {
      return this._defaultMagnet
    } else if (this.magnets.length > 0) {
      return this.magnets[0]
    } else {
      return undefined
    }
  },

  getParentShape: function() {
    return this.parent
  },

  getIncomingShapes: function(iterator) {
    if (iterator) {
      this.incoming.each(iterator)
    }
    return this.incoming
  },

  getIncomingNodes: function(iterator) {
    return this.incoming.select(function(incoming) {
      var isNode = incoming instanceof ORYX.Core.Node
      if (isNode && iterator) iterator(incoming)
      return isNode
    })
  },

  getOutgoingShapes: function(iterator) {
    if (iterator) {
      this.outgoing.each(iterator)
    }
    return this.outgoing
  },

  getOutgoingNodes: function(iterator) {
    return this.outgoing.select(function(out) {
      var isNode = out instanceof ORYX.Core.Node
      if (isNode && iterator) iterator(out)
      return isNode
    })
  },

  getAllDockedShapes: function(iterator) {
    var result = this.incoming.concat(this.outgoing)
    if (iterator) {
      result.each(iterator)
    }
    return result
  },

  getCanvas: function() {
    if (this.parent instanceof ORYX.Core.Canvas) {
      return this.parent
    } else if (this.parent instanceof ORYX.Core.Shape) {
      return this.parent.getCanvas()
    } else {
      return undefined
    }
  },

  /**
   *
   * @param {Object} deep
   * @param {Object} iterator
   */
  getChildNodes: function(deep, iterator) {
    if (!deep && !iterator) {
      return this.nodes.clone()
    } else {
      var result = []
      this.nodes.each(function(uiObject) {
        if (!uiObject.isVisible) {
          return
        }
        if (iterator) {
          iterator(uiObject)
        }
        result.push(uiObject)

        if (deep && uiObject instanceof ORYX.Core.Shape) {
          result = result.concat(uiObject.getChildNodes(deep, iterator))
        }
      })

      return result
    }
  },

  /**
   * Overrides the UIObject.add method. Adds uiObject to the correct sub node.
   * @param {UIObject} uiObject
   * @param {Number} index
   */
  add: function(uiObject, index, silent) {
    //parameter has to be an UIObject, but
    // must not be an Edge.
    if (uiObject instanceof ORYX.Core.UIObject && !(uiObject instanceof ORYX.Core.Edge)) {
      if (!this.children.member(uiObject)) {
        //if uiObject is child of another parent, remove it from that parent.
        if (uiObject.parent) {
          uiObject.parent.remove(uiObject, true)
        }

        //add uiObject to this Shape
        if (index != undefined) this.children.splice(index, 0, uiObject)
        else this.children.push(uiObject)

        //set parent reference
        uiObject.parent = this

        //add uiObject.node to this.node depending on the type of uiObject
        var parent
        if (uiObject instanceof ORYX.Core.Node) {
          parent = this.node.childNodes[0].childNodes[1]
          this.nodes.push(uiObject)
        } else if (uiObject instanceof ORYX.Core.Controls.Control) {
          var ctrls = this.node.childNodes[1]
          if (uiObject instanceof ORYX.Core.Controls.Docker) {
            parent = ctrls.childNodes[0]
            if (this.dockers.length >= 2) {
              this.dockers.splice(
                index !== undefined ? Math.min(index, this.dockers.length - 1) : this.dockers.length - 1,
                0,
                uiObject
              )
            } else {
              this.dockers.push(uiObject)
            }
          } else if (uiObject instanceof ORYX.Core.Controls.Magnet) {
            parent = ctrls.childNodes[1]
            this.magnets.push(uiObject)
          } else {
            parent = ctrls
          }
        } else {
          //UIObject
          parent = this.node
        }

        if (index != undefined && index < parent.childNodes.length)
          uiObject.node = parent.insertBefore(uiObject.node, parent.childNodes[index])
        else uiObject.node = parent.appendChild(uiObject.node)

        this._changed()
        //uiObject.bounds.registerCallback(this._changedCallback);

        if (this.eventHandlerCallback && silent !== true)
          this.eventHandlerCallback({ type: ORYX.CONFIG.EVENT_SHAPEADDED, shape: uiObject })
      } else {
        ORYX.Log.warn('add: ORYX.Core.UIObject is already a child of this object.')
      }
    } else {
      ORYX.Log.warn('add: Parameter is not of type ORYX.Core.UIObject.')
    }
  },

  /**
   * Overrides the UIObject.remove method. Removes uiObject.
   * @param {UIObject} uiObject
   */
  remove: function(uiObject, silent) {
    //if uiObject is a child of this object, remove it.
    if (this.children.member(uiObject)) {
      //remove uiObject from children
      var parent = uiObject.parent

      this.children = this.children.without(uiObject)

      //delete parent reference of uiObject
      uiObject.parent = undefined

      //delete uiObject.node from this.node
      if (uiObject instanceof ORYX.Core.Shape) {
        if (uiObject instanceof ORYX.Core.Edge) {
          uiObject.removeMarkers()
          uiObject.node = this.node.childNodes[0].childNodes[2].removeChild(uiObject.node)
        } else {
          uiObject.node = this.node.childNodes[0].childNodes[1].removeChild(uiObject.node)
          this.nodes = this.nodes.without(uiObject)
        }
      } else if (uiObject instanceof ORYX.Core.Controls.Control) {
        if (uiObject instanceof ORYX.Core.Controls.Docker) {
          uiObject.node = this.node.childNodes[1].childNodes[0].removeChild(uiObject.node)
          this.dockers = this.dockers.without(uiObject)
        } else if (uiObject instanceof ORYX.Core.Controls.Magnet) {
          uiObject.node = this.node.childNodes[1].childNodes[1].removeChild(uiObject.node)
          this.magnets = this.magnets.without(uiObject)
        } else {
          uiObject.node = this.node.childNodes[1].removeChild(uiObject.node)
        }
      }

      if (this.eventHandlerCallback && silent !== true)
        this.eventHandlerCallback({ type: ORYX.CONFIG.EVENT_SHAPEREMOVED, shape: uiObject, parent: parent })

      this._changed()
      //uiObject.bounds.unregisterCallback(this._changedCallback);
    } else {
      ORYX.Log.warn('remove: ORYX.Core.UIObject is not a child of this object.')
    }
  },

  /**
   * Calculate the Border Intersection Point between two points
   * @param {PointA}
   * @param {PointB}
   */
  getIntersectionPoint: function() {
    var pointAX, pointAY, pointBX, pointBY

    // Get the the two Points
    switch (arguments.length) {
      case 2:
        pointAX = arguments[0].x
        pointAY = arguments[0].y
        pointBX = arguments[1].x
        pointBY = arguments[1].y
        break
      case 4:
        pointAX = arguments[0]
        pointAY = arguments[1]
        pointBX = arguments[2]
        pointBY = arguments[3]
        break
      default:
        throw 'getIntersectionPoints needs two or four arguments'
    }

    // Defined an include and exclude point
    var includePointX, includePointY, excludePointX, excludePointY

    var bounds = this.absoluteBounds()

    if (this.isPointIncluded(pointAX, pointAY, bounds)) {
      includePointX = pointAX
      includePointY = pointAY
    } else {
      excludePointX = pointAX
      excludePointY = pointAY
    }

    if (this.isPointIncluded(pointBX, pointBY, bounds)) {
      includePointX = pointBX
      includePointY = pointBY
    } else {
      excludePointX = pointBX
      excludePointY = pointBY
    }

    // If there is no inclue or exclude Shape, than return
    if (!includePointX || !includePointY || !excludePointX || !excludePointY) {
      return undefined
    }

    var midPointX = 0
    var midPointY = 0

    var refPointX, refPointY

    var minDifferent = 1
    // Get the UpperLeft and LowerRight
    //var ul = bounds.upperLeft();
    //var lr = bounds.lowerRight();

    var i = 0

    while (true) {
      // Calculate the midpoint of the current to points
      var midPointX =
        Math.min(includePointX, excludePointX) +
        (Math.max(includePointX, excludePointX) - Math.min(includePointX, excludePointX)) / 2.0
      var midPointY =
        Math.min(includePointY, excludePointY) +
        (Math.max(includePointY, excludePointY) - Math.min(includePointY, excludePointY)) / 2.0

      // Set the new midpoint by the means of the include of the bounds
      if (this.isPointIncluded(midPointX, midPointY, bounds)) {
        includePointX = midPointX
        includePointY = midPointY
      } else {
        excludePointX = midPointX
        excludePointY = midPointY
      }

      // Calc the length of the line
      var length = Math.sqrt(Math.pow(includePointX - excludePointX, 2) + Math.pow(includePointY - excludePointY, 2))
      // Calc a point one step from the include point
      ;(refPointX = includePointX + (excludePointX - includePointX) / length),
        (refPointY = includePointY + (excludePointY - includePointY) / length)

      // If the reference point not in the bounds, break
      if (!this.isPointIncluded(refPointX, refPointY, bounds)) {
        break
      }
    }

    // Return the last includepoint
    return { x: refPointX, y: refPointY }
  },

  /**
   * Calculate if the point is inside the Shape
   * @param {PointX}
   * @param {PointY}
   */
  isPointIncluded: function() {
    return false
  },

  /**
   * Returns TRUE if the given node
   * is a child node of the shapes node
   * @param {Element} node
   * @return {Boolean}
   *
   */
  containsNode: function(node) {
    var me = this.node.firstChild.firstChild
    while (node) {
      if (node == me) {
        return true
      }
      node = node.parentNode
    }
    return false
  },

  /**
   * Calculate if the point is over an special offset area
   * @param {Point}
   */
  isPointOverOffset: function() {
    return this.isPointIncluded.apply(this, arguments)
  },

  _dockerChanged: function() {},

  /**
   * Create a Docker for this Edge
   *
   */
  createDocker: function(index, position) {
    var docker = new ORYX.Core.Controls.Docker({ eventHandlerCallback: this.eventHandlerCallback })
    docker.bounds.registerCallback(this._dockerChangedCallback)
    if (position) {
      docker.bounds.centerMoveTo(position)
    }
    this.add(docker, index)

    return docker
  },

  /**
   * Get the serialized object
   * return Array with hash-entrees (prefix, name, value)
   * Following values will given:
   * 		Bounds
   * 		Outgoing Shapes
   * 		Parent
   */
  serialize: function() {
    var serializedObject = arguments.callee.$.serialize.apply(this)

    // Add the bounds
    serializedObject.push({ name: 'bounds', prefix: 'oryx', value: this.bounds.serializeForERDF(), type: 'literal' })

    // Add the outgoing shapes
    this.getOutgoingShapes().each(
      function(followingShape) {
        serializedObject.push({
          name: 'outgoing',
          prefix: 'raziel',
          value: '#' + ERDF.__stripHashes(followingShape.resourceId),
          type: 'resource'
        })
      }.bind(this)
    )

    // Add the parent shape, if the parent not the canvas
    //if(this.parent instanceof ORYX.Core.Shape){
    serializedObject.push({
      name: 'parent',
      prefix: 'raziel',
      value: '#' + ERDF.__stripHashes(this.parent.resourceId),
      type: 'resource'
    })
    //}

    return serializedObject
  },

  deserialize: function(serialize, json) {
    arguments.callee.$.deserialize.apply(this, arguments)

    // Set the Bounds
    var bounds = serialize.find(function(ser) {
      return 'oryx-bounds' === ser.prefix + '-' + ser.name
    })
    if (bounds) {
      var b = bounds.value
        .replace(/,/g, ' ')
        .split(' ')
        .without('')
      if (this instanceof ORYX.Core.Edge) {
        if (!this.dockers.first().isChanged)
          this.dockers.first().bounds.centerMoveTo(parseFloat(b[0]), parseFloat(b[1]))
        if (!this.dockers.last().isChanged) this.dockers.last().bounds.centerMoveTo(parseFloat(b[2]), parseFloat(b[3]))
      } else {
        this.bounds.set(parseFloat(b[0]), parseFloat(b[1]), parseFloat(b[2]), parseFloat(b[3]))
      }
    }

    if (json && json.labels instanceof Array) {
      json.labels.each(
        function(slabel) {
          var label = this.getLabel(slabel.ref)
          if (label) {
            label.deserialize(slabel, this)
          }
        }.bind(this)
      )
    }
  },

  toJSON: function() {
    var json = arguments.callee.$.toJSON.apply(this, arguments)

    var labels = [],
      id = this.id
    this._labels.each(function(obj) {
      var slabel = obj.value.serialize()
      if (slabel) {
        slabel.ref = obj.key.replace(id, '')
        labels.push(slabel)
      }
    })

    if (labels.length > 0) {
      json.labels = labels
    }
    return json
  },

  /**
   * Private methods.
   */

  /**
   * Child classes have to overwrite this method for initializing a loaded
   * SVG representation.
   * @param {SVGDocument} svgDocument
   */
  _init: function(svgDocument) {
    //adjust ids
    this._adjustIds(svgDocument, 0)
  },

  _adjustIds: function(element, idIndex) {
    if (element instanceof Element) {
      var eid = element.getAttributeNS(null, 'id')
      if (eid && eid !== '') {
        element.setAttributeNS(null, 'id', this.id + eid)
      } else {
        element.setAttributeNS(null, 'id', this.id + '_' + this.id + '_' + idIndex)
        idIndex++
      }

      // Replace URL in fill attribute
      var fill = element.getAttributeNS(null, 'fill')
      if (fill && fill.include('url(#')) {
        fill = fill.replace(/url\(#/g, 'url(#' + this.id)
        element.setAttributeNS(null, 'fill', fill)
      }

      if (element.hasChildNodes()) {
        for (var i = 0; i < element.childNodes.length; i++) {
          idIndex = this._adjustIds(element.childNodes[i], idIndex)
        }
      }
    }
    return idIndex
  },

  toString: function() {
    return 'ORYX.Core.Shape ' + this.getId()
  }
}
ORYX.Core.Shape = ORYX.Core.AbstractShape.extend(ORYX.Core.Shape)
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

/**
 * Init namespaces
 */
if (!ORYX) {
  var ORYX = {}
}
if (!ORYX.Core) {
  ORYX.Core = {}
}
if (!ORYX.Core.Controls) {
  ORYX.Core.Controls = {}
}

/**
 * @classDescription Abstract base class for all Controls.
 */
ORYX.Core.Controls.Control = ORYX.Core.UIObject.extend({
  toString: function() {
    return 'Control ' + this.id
  }
})
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

/**
 * Init namespaces
 */
if (!ORYX) {
  var ORYX = {}
}
if (!ORYX.Core) {
  ORYX.Core = {}
}
if (!ORYX.Core.Controls) {
  ORYX.Core.Controls = {}
}

/**
 * @classDescription Represents a movable docker that can be bound to a shape. Dockers are used
 * for positioning shape objects.
 * @extends {Control}
 *
 * TODO absoluteXY und absoluteCenterXY von einem Docker liefern falsche Werte!!!
 */
ORYX.Core.Controls.Docker = ORYX.Core.Controls.Control.extend({
  /**
   * Constructor
   */
  construct: function() {
    arguments.callee.$.construct.apply(this, arguments)

    this.isMovable = true // Enables movability
    this.bounds.set(0, 0, 16, 16) // Set the bounds
    this.referencePoint = undefined // Refrenzpoint
    this._dockedShapeBounds = undefined
    this._dockedShape = undefined
    this._oldRefPoint1 = undefined
    this._oldRefPoint2 = undefined

    //this.anchors = [];
    this.anchorLeft
    this.anchorRight
    this.anchorTop
    this.anchorBottom

    this.node = ORYX.Editor.graft('http://www.w3.org/2000/svg', null, ['g'])

    // The DockerNode reprasentation
    this._dockerNode = ORYX.Editor.graft('http://www.w3.org/2000/svg', this.node, [
      'g',
      { 'pointer-events': 'all' },
      ['circle', { cx: '8', cy: '8', r: '8', stroke: 'none', fill: 'none' }],
      ['circle', { cx: '8', cy: '8', r: '3', stroke: 'black', fill: 'red', 'stroke-width': '1' }]
    ])

    // The ReferenzNode reprasentation
    this._referencePointNode = ORYX.Editor.graft('http://www.w3.org/2000/svg', this.node, [
      'g',
      { 'pointer-events': 'none' },
      [
        'circle',
        { cx: this.bounds.upperLeft().x, cy: this.bounds.upperLeft().y, r: 3, fill: 'red', 'fill-opacity': 0.4 }
      ]
    ])

    // Hide the Docker
    this.hide()

    //Add to the EventHandler
    this.addEventHandlers(this._dockerNode)

    // Buffer the Update Callback for un-/register on Event-Handler
    this._updateCallback = this._changed.bind(this)
  },

  update: function() {
    // If there have an DockedShape
    if (this._dockedShape) {
      if (this._dockedShapeBounds && this._dockedShape instanceof ORYX.Core.Node) {
        // Calc the delta of width and height of the lastBounds and the current Bounds
        var dswidth = this._dockedShapeBounds.width()
        var dsheight = this._dockedShapeBounds.height()
        if (!dswidth) dswidth = 1
        if (!dsheight) dsheight = 1
        var widthDelta = this._dockedShape.bounds.width() / dswidth
        var heightDelta = this._dockedShape.bounds.height() / dsheight

        // If there is an different
        if (widthDelta !== 1.0 || heightDelta !== 1.0) {
          // Set the delta
          this.referencePoint.x *= widthDelta
          this.referencePoint.y *= heightDelta
        }

        // Clone these bounds
        this._dockedShapeBounds = this._dockedShape.bounds.clone()
      }

      // Get the first and the last Docker of the parent Shape
      var dockerIndex = this.parent.dockers.indexOf(this)
      var dock1 = this
      var dock2 =
        this.parent.dockers.length > 1
          ? dockerIndex === 0 // If there is the first element
            ? this.parent.dockers[dockerIndex + 1] // then take the next docker
            : this.parent.dockers[dockerIndex - 1] // if not, then take the docker before
          : undefined

      // Calculate the first absolute Refenzpoint
      var absoluteReferenzPoint1 = dock1.getDockedShape() ? dock1.getAbsoluteReferencePoint() : dock1.bounds.center()

      // Calculate the last absolute Refenzpoint
      var absoluteReferenzPoint2 =
        dock2 && dock2.getDockedShape() ? dock2.getAbsoluteReferencePoint() : dock2 ? dock2.bounds.center() : undefined

      // If there is no last absolute Referenzpoint
      if (!absoluteReferenzPoint2) {
        // Calculate from the middle of the DockedShape
        var center = this._dockedShape.absoluteCenterXY()
        var minDimension = this._dockedShape.bounds.width() * this._dockedShape.bounds.height()
        absoluteReferenzPoint2 = {
          x: absoluteReferenzPoint1.x + (center.x - absoluteReferenzPoint1.x) * -minDimension,
          y: absoluteReferenzPoint1.y + (center.y - absoluteReferenzPoint1.y) * -minDimension
        }
      }

      var newPoint = undefined

      /*if (!this._oldRefPoint1 || !this._oldRefPoint2 ||
				absoluteReferenzPoint1.x !== this._oldRefPoint1.x ||
				absoluteReferenzPoint1.y !== this._oldRefPoint1.y ||
				absoluteReferenzPoint2.x !== this._oldRefPoint2.x ||
				absoluteReferenzPoint2.y !== this._oldRefPoint2.y) {*/

      // Get the new point for the Docker, calucalted by the intersection point of the Shape and the two points
      newPoint = this._dockedShape.getIntersectionPoint(absoluteReferenzPoint1, absoluteReferenzPoint2)

      // If there is new point, take the referencepoint as the new point
      if (!newPoint) {
        newPoint = this.getAbsoluteReferencePoint()
      }

      if (this.parent && this.parent.parent) {
        var grandParentPos = this.parent.parent.absoluteXY()
        newPoint.x -= grandParentPos.x
        newPoint.y -= grandParentPos.y
      }

      // Set the bounds to the new point
      this.bounds.centerMoveTo(newPoint)

      this._oldRefPoint1 = absoluteReferenzPoint1
      this._oldRefPoint2 = absoluteReferenzPoint2
    }
    /*else {
				newPoint = this.bounds.center();
			}*/

    //	}

    // Call the super class
    arguments.callee.$.update.apply(this, arguments)
  },

  /**
   * Calls the super class refresh method and updates the view of the docker.
   */
  refresh: function() {
    arguments.callee.$.refresh.apply(this, arguments)

    // Refresh the dockers node
    var p = this.bounds.upperLeft()
    this._dockerNode.setAttributeNS(null, 'transform', 'translate(' + p.x + ', ' + p.y + ')')

    // Refresh the referencepoints node
    p = Object.clone(this.referencePoint)

    if (p && this._dockedShape) {
      var upL
      if (this.parent instanceof ORYX.Core.Edge) {
        upL = this._dockedShape.absoluteXY()
      } else {
        upL = this._dockedShape.bounds.upperLeft()
      }
      p.x += upL.x
      p.y += upL.y
    } else {
      p = this.bounds.center()
    }

    this._referencePointNode.setAttributeNS(null, 'transform', 'translate(' + p.x + ', ' + p.y + ')')
  },

  /**
   * Set the reference point
   * @param {Object} point
   */

  setReferencePoint: function(point) {
    // Set the referencepoint
    if (
      this.referencePoint !== point &&
      (!this.referencePoint || !point || this.referencePoint.x !== point.x || this.referencePoint.y !== point.y)
    ) {
      this.referencePoint = point
      this._changed()
    }

    // Update directly, because the referencepoint has no influence of the bounds
    //this.refresh();
  },

  /**
   * Get the absolute referencepoint
   */
  getAbsoluteReferencePoint: function() {
    if (!this.referencePoint || !this._dockedShape) {
      return undefined
    } else {
      var absUL = this._dockedShape.absoluteXY()
      return {
        x: this.referencePoint.x + absUL.x,
        y: this.referencePoint.y + absUL.y
      }
    }
  },

  /**
   * Set the docked Shape from the docker
   * @param {Object} shape
   */
  setDockedShape: function(shape) {
    // If there is an old docked Shape
    if (this._dockedShape) {
      this._dockedShape.bounds.unregisterCallback(this._updateCallback)

      // Delete the Shapes from the incoming and outgoing array
      // If this Docker the incoming of the Shape
      if (this === this.parent.dockers.first()) {
        this.parent.incoming = this.parent.incoming.without(this._dockedShape)
        this._dockedShape.outgoing = this._dockedShape.outgoing.without(this.parent)

        // If this Docker the outgoing of the Shape
      } else if (this === this.parent.dockers.last()) {
        this.parent.outgoing = this.parent.outgoing.without(this._dockedShape)
        this._dockedShape.incoming = this._dockedShape.incoming.without(this.parent)
      }
    }

    // Set the new Shape
    this._dockedShape = shape
    this._dockedShapeBounds = undefined
    var referencePoint = undefined

    // If there is an Shape, register the updateCallback if there are changes in the shape bounds
    if (this._dockedShape) {
      // Add the Shapes to the incoming and outgoing array
      // If this Docker the incoming of the Shape
      if (this === this.parent.dockers.first()) {
        this.parent.incoming.push(shape)
        shape.outgoing.push(this.parent)

        // If this Docker the outgoing of the Shape
      } else if (this === this.parent.dockers.last()) {
        this.parent.outgoing.push(shape)
        shape.incoming.push(this.parent)
      }

      // Get the bounds and set the new referencepoint
      var bounds = this.bounds
      var absUL = shape.absoluteXY()

      /*if(shape.parent){
				var b = shape.parent.bounds.upperLeft();
				absUL.x -= b.x;
				absUL.y -= b.y;
			}*/

      referencePoint = {
        x: bounds.center().x - absUL.x,
        y: bounds.center().y - absUL.y
      }

      this._dockedShapeBounds = this._dockedShape.bounds.clone()

      this._dockedShape.bounds.registerCallback(this._updateCallback)

      // Set the color of the docker as docked
      this.setDockerColor(ORYX.CONFIG.DOCKER_DOCKED_COLOR)
    } else {
      // Set the color of the docker as undocked
      this.setDockerColor(ORYX.CONFIG.DOCKER_UNDOCKED_COLOR)
    }

    // Set the referencepoint
    this.setReferencePoint(referencePoint)
    this._changed()
    //this.update();
  },

  /**
   * Get the docked Shape
   */
  getDockedShape: function() {
    return this._dockedShape
  },

  /**
   * Returns TRUE if the docker has a docked shape
   */
  isDocked: function() {
    return !!this._dockedShape
  },

  /**
   * Set the Color of the Docker
   * @param {Object} color
   */
  setDockerColor: function(color) {
    this._dockerNode.lastChild.setAttributeNS(null, 'fill', color)
  },

  preventHiding: function(prevent) {
    this._preventHiding = Math.max(0, (this._preventHiding || 0) + (prevent ? 1 : -1))
  },

  /**
   * Hides this UIObject and all its children.
   */
  hide: function() {
    if (this._preventHiding) {
      return false
    }

    // Hide docker and reference point
    this.node.setAttributeNS(null, 'visibility', 'hidden')
    this._referencePointNode.setAttributeNS(null, 'visibility', 'hidden')

    this.children.each(function(uiObj) {
      uiObj.hide()
    })
  },

  /**
   * Enables visibility of this UIObject and all its children.
   */
  show: function() {
    // Show docker
    this.node.setAttributeNS(null, 'visibility', 'visible')

    // Hide reference point if the connected shape is an edge
    if (this.getDockedShape() instanceof ORYX.Core.Edge) {
      this._referencePointNode.setAttributeNS(null, 'visibility', 'hidden')
    } else {
      this._referencePointNode.setAttributeNS(null, 'visibility', 'visible')
    }

    this.children.each(function(uiObj) {
      uiObj.show()
    })
  },

  toString: function() {
    return 'Docker ' + this.id
  }
})
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

/**
 * Init namespaces
 */
if (!ORYX) {
  var ORYX = {}
}
if (!ORYX.Core) {
  ORYX.Core = {}
}
if (!ORYX.Core.Controls) {
  ORYX.Core.Controls = {}
}

/**
 * @classDescription Represents a magnet that is part of another shape and can
 * be attached to dockers. Magnets are used for linking edge objects
 * to other Shape objects.
 * @extends {Control}
 */
ORYX.Core.Controls.Magnet = ORYX.Core.Controls.Control.extend({
  /**
   * Constructor
   */
  construct: function() {
    arguments.callee.$.construct.apply(this, arguments)

    //this.anchors = [];
    this.anchorLeft
    this.anchorRight
    this.anchorTop
    this.anchorBottom

    this.bounds.set(0, 0, 16, 16)

    //graft magnet's root node into owner's control group.
    this.node = ORYX.Editor.graft('http://www.w3.org/2000/svg', null, [
      'g',
      { 'pointer-events': 'all' },
      ['circle', { cx: '8', cy: '8', r: '4', stroke: 'none', fill: 'red', 'fill-opacity': '0.3' }]
    ])

    this.hide()
  },

  update: function() {
    arguments.callee.$.update.apply(this, arguments)

    //this.isChanged = true;
  },

  _update: function() {
    arguments.callee.$.update.apply(this, arguments)

    //this.isChanged = true;
  },

  refresh: function() {
    arguments.callee.$.refresh.apply(this, arguments)

    var p = this.bounds.upperLeft()
    /*if(this.parent) {
			var parentPos = this.parent.bounds.upperLeft();
			p.x += parentPos.x;
			p.y += parentPos.y;
		}*/

    this.node.setAttributeNS(null, 'transform', 'translate(' + p.x + ', ' + p.y + ')')
  },

  show: function() {
    //this.refresh();
    arguments.callee.$.show.apply(this, arguments)
  },

  toString: function() {
    return 'Magnet ' + this.id
  }
})
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

/**
 * Init namespaces
 */
if (!ORYX) {
  var ORYX = {}
}
if (!ORYX.Core) {
  ORYX.Core = {}
}

/**
 * @classDescription Abstract base class for all Nodes.
 * @extends ORYX.Core.Shape
 */
ORYX.Core.Node = {
  /**
   * Constructor
   * @param options {Object} A container for arguments.
   * @param stencil {Stencil}
   */
  construct: function(options, stencil, facade) {
    arguments.callee.$.construct.apply(this, arguments)

    this.isSelectable = true
    this.isMovable = true
    this._dockerUpdated = false
    this.facade = facade

    this._oldBounds = new ORYX.Core.Bounds() //init bounds with undefined values
    this._svgShapes = [] //array of all SVGShape objects of
    // SVG representation

    //TODO vielleicht in shape verschieben?
    this.minimumSize = undefined // {width:..., height:...}
    this.maximumSize = undefined

    //TODO vielleicht in shape oder uiobject verschieben?
    // vielleicht sogar isResizable ersetzen?
    this.isHorizontallyResizable = false
    this.isVerticallyResizable = false

    this.dataId = undefined

    this._init(this._stencil.view())
    this.forcedHeight = -1
  },

  /**
   * This method checks whether the shape is resized correctly and calls the
   * super class update method.
   */
  _update: function() {
    this.dockers.invoke('update')
    if (this.isChanged) {
      var bounds = this.bounds
      var oldBounds = this._oldBounds

      if (this.isResized) {
        var widthDelta = bounds.width() / oldBounds.width()
        var heightDelta = bounds.height() / oldBounds.height()

        //iterate over all relevant svg elements and resize them
        this._svgShapes.each(function(svgShape) {
          //adjust width
          if (svgShape.isHorizontallyResizable) {
            svgShape.width = svgShape.oldWidth * widthDelta
          }
          //adjust height
          if (svgShape.isVerticallyResizable) {
            svgShape.height = svgShape.oldHeight * heightDelta
          }

          //check, if anchors are set
          var anchorOffset
          var leftIncluded = svgShape.anchorLeft
          var rightIncluded = svgShape.anchorRight

          if (rightIncluded) {
            anchorOffset = oldBounds.width() - (svgShape.oldX + svgShape.oldWidth)
            if (leftIncluded) {
              svgShape.width = bounds.width() - svgShape.x - anchorOffset
            } else {
              svgShape.x = bounds.width() - (anchorOffset + svgShape.width)
            }
          } else if (!leftIncluded) {
            svgShape.x = widthDelta * svgShape.oldX
            if (!svgShape.isHorizontallyResizable) {
              svgShape.x = svgShape.x + (svgShape.width * widthDelta) / 2 - svgShape.width / 2
            }
          }

          var topIncluded = svgShape.anchorTop
          var bottomIncluded = svgShape.anchorBottom

          if (bottomIncluded) {
            anchorOffset = oldBounds.height() - (svgShape.oldY + svgShape.oldHeight)
            if (topIncluded) {
              svgShape.height = bounds.height() - svgShape.y - anchorOffset
            } else {
              // Hack for choreography task layouting
              if (!svgShape._isYLocked) {
                svgShape.y = bounds.height() - (anchorOffset + svgShape.height)
              }
            }
          } else if (!topIncluded) {
            svgShape.y = heightDelta * svgShape.oldY
            if (!svgShape.isVerticallyResizable) {
              svgShape.y = svgShape.y + (svgShape.height * heightDelta) / 2 - svgShape.height / 2
            }
          }
        })

        //check, if the current bounds is unallowed horizontally or vertically resized
        var p = {
          x: 0,
          y: 0
        }
        if (!this.isHorizontallyResizable && bounds.width() !== oldBounds.width()) {
          p.x = oldBounds.width() - bounds.width()
        }
        if (!this.isVerticallyResizable && bounds.height() !== oldBounds.height()) {
          p.y = oldBounds.height() - bounds.height()
        }
        if (p.x !== 0 || p.y !== 0) {
          bounds.extend(p)
        }

        //check, if the current bounds are between maximum and minimum bounds
        p = {
          x: 0,
          y: 0
        }
        var widthDifference, heightDifference
        if (this.minimumSize) {
          ORYX.Log.debug("Shape (%0)'s min size: (%1x%2)", this, this.minimumSize.width, this.minimumSize.height)
          widthDifference = this.minimumSize.width - bounds.width()
          if (widthDifference > 0) {
            p.x += widthDifference
          }
          heightDifference = this.minimumSize.height - bounds.height()
          if (heightDifference > 0) {
            p.y += heightDifference
          }
        }
        if (this.maximumSize) {
          ORYX.Log.debug("Shape (%0)'s max size: (%1x%2)", this, this.maximumSize.width, this.maximumSize.height)
          widthDifference = bounds.width() - this.maximumSize.width
          if (widthDifference > 0) {
            p.x -= widthDifference
          }
          heightDifference = bounds.height() - this.maximumSize.height
          if (heightDifference > 0) {
            p.y -= heightDifference
          }
        }
        if (p.x !== 0 || p.y !== 0) {
          bounds.extend(p)
        }

        //update magnets

        var widthDelta = bounds.width() / oldBounds.width()
        var heightDelta = bounds.height() / oldBounds.height()

        var leftIncluded, rightIncluded, topIncluded, bottomIncluded, center, newX, newY

        this.magnets.each(function(magnet) {
          leftIncluded = magnet.anchorLeft
          rightIncluded = magnet.anchorRight
          topIncluded = magnet.anchorTop
          bottomIncluded = magnet.anchorBottom

          center = magnet.bounds.center()

          if (leftIncluded) {
            newX = center.x
          } else if (rightIncluded) {
            newX = bounds.width() - (oldBounds.width() - center.x)
          } else {
            newX = center.x * widthDelta
          }

          if (topIncluded) {
            newY = center.y
          } else if (bottomIncluded) {
            newY = bounds.height() - (oldBounds.height() - center.y)
          } else {
            newY = center.y * heightDelta
          }

          if (center.x !== newX || center.y !== newY) {
            magnet.bounds.centerMoveTo(newX, newY)
          }
        })

        //set new position of labels
        this.getLabels().each(function(label) {
          // Set the position dependings on it anchor
          if (!label.isAnchorLeft()) {
            if (label.isAnchorRight()) {
              label.setX(bounds.width() - (oldBounds.width() - label.oldX))
            } else {
              label.setX((label.position ? label.position.x : label.x) * widthDelta)
            }
          }
          if (!label.isAnchorTop()) {
            if (label.isAnchorBottom()) {
              label.setY(bounds.height() - (oldBounds.height() - label.oldY))
            } else {
              label.setY((label.position ? label.position.y : label.y) * heightDelta)
            }
          }

          // If there is an position,
          // set the origin position as well
          if (label.position) {
            if (!label.isOriginAnchorLeft()) {
              if (label.isOriginAnchorRight()) {
                label.setOriginX(bounds.width() - (oldBounds.width() - label.oldX))
              } else {
                label.setOriginX(label.x * widthDelta)
              }
            }
            if (!label.isOriginAnchorTop()) {
              if (label.isOriginAnchorBottom()) {
                label.setOriginY(bounds.height() - (oldBounds.height() - label.oldY))
              } else {
                label.setOriginY(label.y * heightDelta)
              }
            }
          }
        })

        //update docker
        var docker = this.dockers[0]
        if (docker) {
          docker.bounds.unregisterCallback(this._dockerChangedCallback)
          if (!this._dockerUpdated) {
            docker.bounds.centerMoveTo(this.bounds.center())
            this._dockerUpdated = false
          }

          docker.update()
          docker.bounds.registerCallback(this._dockerChangedCallback)
        }
        this.isResized = false
      }

      this.refresh()

      this.isChanged = false

      this._oldBounds = this.bounds.clone()
    }

    this.children.each(function(value) {
      if (!(value instanceof ORYX.Core.Controls.Docker)) {
        value._update()
      }
    })

    if (this.dockers.length > 0 && !this.dockers.first().getDockedShape()) {
      this.dockers.each(
        function(docker) {
          docker.bounds.centerMoveTo(this.bounds.center())
        }.bind(this)
      )
    }

    /*this.incoming.each((function(edge) {
			if(!(this.dockers[0] && this.dockers[0].getDockedShape() instanceof ORYX.Core.Node))
				edge._update(true);
		}).bind(this));
		
		this.outgoing.each((function(edge) {
			if(!(this.dockers[0] && this.dockers[0].getDockedShape() instanceof ORYX.Core.Node))
				edge._update(true);
		}).bind(this)); */
  },

  /**
   * This method repositions and resizes the SVG representation
   * of the shape.
   */
  refresh: function() {
    arguments.callee.$.refresh.apply(this, arguments)

    /** Movement */
    var x = this.bounds.upperLeft().x
    var y = this.bounds.upperLeft().y

    // Move owner element
    this.node.firstChild.setAttributeNS(null, 'transform', 'translate(' + x + ', ' + y + ')')
    // Move magnets
    this.node.childNodes[1].childNodes[1].setAttributeNS(null, 'transform', 'translate(' + x + ', ' + y + ')')

    /** Resize */

    //iterate over all relevant svg elements and update them
    this._svgShapes.each(function(svgShape) {
      svgShape.update()
    })
  },

  _dockerChanged: function() {
    var docker = this.dockers[0]

    //set the bounds of the the association
    this.bounds.centerMoveTo(docker.bounds.center())

    this._dockerUpdated = true
    //this._update(true);
  },

  /**
   * This method traverses a tree of SVGElements and returns
   * all SVGShape objects. For each basic shape or path element
   * a SVGShape object is initialized.
   *
   * @param svgNode {SVGElement}
   * @return {Array} Array of SVGShape objects
   */
  _initSVGShapes: function(svgNode) {
    var svgShapes = []
    try {
      var svgShape = new ORYX.Core.SVG.SVGShape(svgNode)
      svgShapes.push(svgShape)
    } catch (e) {
      //do nothing
    }

    if (svgNode.hasChildNodes()) {
      for (var i = 0; i < svgNode.childNodes.length; i++) {
        svgShapes = svgShapes.concat(this._initSVGShapes(svgNode.childNodes[i]))
      }
    }

    return svgShapes
  },

  /**
   * Calculate if the point is inside the Shape
   * @param {PointX}
   * @param {PointY}
   * @param {absoluteBounds} optional: for performance
   */
  isPointIncluded: function(pointX, pointY, absoluteBounds) {
    // If there is an arguments with the absoluteBounds
    var absBounds =
      absoluteBounds && absoluteBounds instanceof ORYX.Core.Bounds ? absoluteBounds : this.absoluteBounds()

    if (!absBounds.isIncluded(pointX, pointY)) {
      return false
    } else {
    }

    //point = Object.clone(point);
    var ul = absBounds.upperLeft()
    var x = pointX - ul.x
    var y = pointY - ul.y

    var i = 0
    do {
      var isPointIncluded = this._svgShapes[i++].isPointIncluded(x, y)
    } while (!isPointIncluded && i < this._svgShapes.length)

    return isPointIncluded

    /*return this._svgShapes.any(function(svgShape){
            return svgShape.isPointIncluded(point);
        });*/
  },

  /**
   * Calculate if the point is over an special offset area
   * @param {Point}
   */
  isPointOverOffset: function(pointX, pointY) {
    var isOverEl = arguments.callee.$.isPointOverOffset.apply(this, arguments)

    if (isOverEl) {
      // If there is an arguments with the absoluteBounds
      var absBounds = this.absoluteBounds()
      absBounds.widen(-ORYX.CONFIG.BORDER_OFFSET)

      if (!absBounds.isIncluded(pointX, pointY)) {
        return true
      }
    }

    return false
  },

  serialize: function() {
    var result = arguments.callee.$.serialize.apply(this)

    // Add the docker's bounds
    // nodes only have at most one docker!
    this.dockers.each(
      function(docker) {
        if (docker.getDockedShape()) {
          var center = docker.referencePoint
          center = center ? center : docker.bounds.center()
          result.push({
            name: 'docker',
            prefix: 'oryx',
            value: $H(center)
              .values()
              .join(','),
            type: 'literal'
          })
        }
      }.bind(this)
    )

    // Get the spezific serialized object from the stencil
    try {
      //result = this.getStencil().serialize(this, result);

      var serializeEvent = this.getStencil().serialize()

      /*
       * call serialize callback by reference, result should be found
       * in serializeEvent.result
       */
      if (serializeEvent.type) {
        serializeEvent.shape = this
        serializeEvent.data = result
        serializeEvent.result = undefined
        serializeEvent.forceExecution = true

        this._delegateEvent(serializeEvent)

        if (serializeEvent.result) {
          result = serializeEvent.result
        }
      }
    } catch (e) {}
    return result
  },

  deserialize: function(data) {
    arguments.callee.$.deserialize.apply(this, arguments)

    try {
      //data = this.getStencil().deserialize(this, data);

      var deserializeEvent = this.getStencil().deserialize()

      /*
       * call serialize callback by reference, result should be found
       * in serializeEventInfo.result
       */
      if (deserializeEvent.type) {
        deserializeEvent.shape = this
        deserializeEvent.data = data
        deserializeEvent.result = undefined
        deserializeEvent.forceExecution = true

        this._delegateEvent(deserializeEvent)
        if (deserializeEvent.result) {
          data = deserializeEvent.result
        }
      }
    } catch (e) {}

    // Set the outgoing shapes
    var outgoing = data.findAll(function(ser) {
      return ser.prefix + '-' + ser.name == 'raziel-outgoing'
    })
    outgoing.each(
      function(obj) {
        // TODO: Look at Canvas
        if (!this.parent) {
          return
        }

        // Set outgoing Shape
        var next = this.getCanvas().getChildShapeByResourceId(obj.value)

        if (next) {
          if (next instanceof ORYX.Core.Edge) {
            //Set the first docker of the next shape
            next.dockers.first().setDockedShape(this)
            next.dockers.first().setReferencePoint(next.dockers.first().bounds.center())
          } else if (next.dockers.length > 0) {
            //next is a node and next has a docker
            next.dockers.first().setDockedShape(this)
            //next.dockers.first().setReferencePoint({x: this.bounds.width() / 2.0, y: this.bounds.height() / 2.0});
          }
        }
      }.bind(this)
    )

    if (this.dockers.length === 1) {
      var dockerPos
      dockerPos = data.find(function(entry) {
        return entry.prefix + '-' + entry.name === 'oryx-dockers'
      })

      if (dockerPos) {
        var points = dockerPos.value
          .replace(/,/g, ' ')
          .split(' ')
          .without('')
          .without('#')
        if (points.length === 2 && this.dockers[0].getDockedShape()) {
          this.dockers[0].setReferencePoint({
            x: parseFloat(points[0]),
            y: parseFloat(points[1])
          })
        } else {
          this.dockers[0].bounds.centerMoveTo(parseFloat(points[0]), parseFloat(points[1]))
        }
      }
    }
  },

  /**
   * This method excepts the SVGDoucment that is the SVG representation
   * of this shape.
   * The bounds of the shape are calculated, the SVG representation's upper left point
   * is moved to 0,0 and it the method sets if this shape is resizable.
   *
   * @param {SVGDocument} svgDocument
   */
  _init: function(svgDocument) {
    arguments.callee.$._init.apply(this, arguments)

    var svgNode = svgDocument.getElementsByTagName('g')[0] //outer most g node
    // set all required attributes
    var attributeTitle = svgDocument.ownerDocument.createAttribute('title')
    attributeTitle.nodeValue = this.getStencil().title()
    svgNode.setAttributeNode(attributeTitle)

    var attributeId = svgDocument.ownerDocument.createAttribute('id')
    attributeId.nodeValue = this.id
    svgNode.setAttributeNode(attributeId)

    //
    var stencilTargetNode = this.node.childNodes[0].childNodes[0] //<g class=me>"
    svgNode = stencilTargetNode.appendChild(svgNode)

    // Add to the EventHandler
    this.addEventHandlers(svgNode.parentNode)

    /**set minimum and maximum size*/
    var minSizeAttr = svgNode.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'minimumSize')
    if (minSizeAttr) {
      minSizeAttr = minSizeAttr.replace('/,/g', ' ')
      var minSizeValues = minSizeAttr.split(' ')
      minSizeValues = minSizeValues.without('')

      if (minSizeValues.length > 1) {
        this.minimumSize = {
          width: parseFloat(minSizeValues[0]),
          height: parseFloat(minSizeValues[1])
        }
      } else {
        //set minimumSize to (1,1), so that width and height of the stencil can never be (0,0)
        this.minimumSize = {
          width: 1,
          height: 1
        }
      }
    }

    var maxSizeAttr = svgNode.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'maximumSize')
    if (maxSizeAttr) {
      maxSizeAttr = maxSizeAttr.replace('/,/g', ' ')
      var maxSizeValues = maxSizeAttr.split(' ')
      maxSizeValues = maxSizeValues.without('')

      if (maxSizeValues.length > 1) {
        this.maximumSize = {
          width: parseFloat(maxSizeValues[0]),
          height: parseFloat(maxSizeValues[1])
        }
      }
    }

    if (
      this.minimumSize &&
      this.maximumSize &&
      (this.minimumSize.width > this.maximumSize.width || this.minimumSize.height > this.maximumSize.height)
    ) {
      //TODO wird verschluckt!!!
      throw this + ': Minimum Size must be greater than maxiumSize.'
    }

    /**get current bounds and adjust it to upperLeft == (0,0)*/
    //initialize all SVGShape objects
    this._svgShapes = this._initSVGShapes(svgNode)

    //get upperLeft and lowerRight of stencil
    var upperLeft = {
      x: undefined,
      y: undefined
    }
    var lowerRight = {
      x: undefined,
      y: undefined
    }
    var me = this
    this._svgShapes.each(function(svgShape) {
      upperLeft.x = upperLeft.x !== undefined ? Math.min(upperLeft.x, svgShape.x) : svgShape.x
      upperLeft.y = upperLeft.y !== undefined ? Math.min(upperLeft.y, svgShape.y) : svgShape.y
      lowerRight.x =
        lowerRight.x !== undefined ? Math.max(lowerRight.x, svgShape.x + svgShape.width) : svgShape.x + svgShape.width
      lowerRight.y =
        lowerRight.y !== undefined ? Math.max(lowerRight.y, svgShape.y + svgShape.height) : svgShape.y + svgShape.height

      /** set if resizing is enabled */
      //TODO isResizable durch die beiden anderen booleans ersetzen?
      if (svgShape.isHorizontallyResizable) {
        me.isHorizontallyResizable = true
        me.isResizable = true
      }
      if (svgShape.isVerticallyResizable) {
        me.isVerticallyResizable = true
        me.isResizable = true
      }
      if (svgShape.anchorTop && svgShape.anchorBottom) {
        me.isVerticallyResizable = true
        me.isResizable = true
      }
      if (svgShape.anchorLeft && svgShape.anchorRight) {
        me.isHorizontallyResizable = true
        me.isResizable = true
      }
    })

    //move all SVGShapes by -upperLeft
    this._svgShapes.each(function(svgShape) {
      svgShape.x -= upperLeft.x
      svgShape.y -= upperLeft.y
      svgShape.update()
    })

    //set bounds of shape
    //the offsets are also needed for positioning the magnets and the docker
    var offsetX = upperLeft.x
    var offsetY = upperLeft.y

    lowerRight.x -= offsetX
    lowerRight.y -= offsetY
    upperLeft.x = 0
    upperLeft.y = 0

    //prevent that width or height of initial bounds is 0
    if (lowerRight.x === 0) {
      lowerRight.x = 1
    }
    if (lowerRight.y === 0) {
      lowerRight.y = 1
    }

    this._oldBounds.set(upperLeft, lowerRight)
    this.bounds.set(upperLeft, lowerRight)

    /**initialize magnets */

    var magnets = svgDocument.getElementsByTagNameNS(ORYX.CONFIG.NAMESPACE_ORYX, 'magnets')

    if (magnets && magnets.length > 0) {
      magnets = $A(magnets[0].getElementsByTagNameNS(ORYX.CONFIG.NAMESPACE_ORYX, 'magnet'))

      var me = this
      magnets.each(function(magnetElem) {
        var magnet = new ORYX.Core.Controls.Magnet({
          eventHandlerCallback: me.eventHandlerCallback
        })
        var cx = parseFloat(magnetElem.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'cx'))
        var cy = parseFloat(magnetElem.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'cy'))
        magnet.bounds.centerMoveTo({
          x: cx - offsetX,
          y: cy - offsetY
        })

        //get anchors
        var anchors = magnetElem.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'anchors')
        if (anchors) {
          anchors = anchors.replace('/,/g', ' ')
          anchors = anchors.split(' ').without('')
          for (var i = 0; i < anchors.length; i++) {
            switch (anchors[i].toLowerCase()) {
              case 'left':
                magnet.anchorLeft = true
                break
              case 'right':
                magnet.anchorRight = true
                break
              case 'top':
                magnet.anchorTop = true
                break
              case 'bottom':
                magnet.anchorBottom = true
                break
            }
          }
        }

        me.add(magnet)

        //check, if magnet is default magnet
        if (!this._defaultMagnet) {
          var defaultAttr = magnetElem.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'default')
          if (defaultAttr && defaultAttr.toLowerCase() === 'yes') {
            me._defaultMagnet = magnet
          }
        }
      })
    } else {
      // Add a Magnet in the Center of Shape
      var magnet = new ORYX.Core.Controls.Magnet()
      magnet.bounds.centerMoveTo(this.bounds.width() / 2, this.bounds.height() / 2)
      this.add(magnet)
    }

    /**initialize docker */
    var dockerElem = svgDocument.getElementsByTagNameNS(ORYX.CONFIG.NAMESPACE_ORYX, 'docker')

    if (dockerElem && dockerElem.length > 0) {
      dockerElem = dockerElem[0]
      var docker = this.createDocker()
      var cx = parseFloat(dockerElem.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'cx'))
      var cy = parseFloat(dockerElem.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'cy'))
      docker.bounds.centerMoveTo({
        x: cx - offsetX,
        y: cy - offsetY
      })

      //get anchors
      var anchors = dockerElem.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'anchors')
      if (anchors) {
        anchors = anchors.replace('/,/g', ' ')
        anchors = anchors.split(' ').without('')

        for (var i = 0; i < anchors.length; i++) {
          switch (anchors[i].toLowerCase()) {
            case 'left':
              docker.anchorLeft = true
              break
            case 'right':
              docker.anchorRight = true
              break
            case 'top':
              docker.anchorTop = true
              break
            case 'bottom':
              docker.anchorBottom = true
              break
          }
        }
      }
    }

    /**initialize labels*/
    var textElems = svgNode.getElementsByTagNameNS(ORYX.CONFIG.NAMESPACE_SVG, 'text')
    $A(textElems).each(
      function(textElem) {
        var label = new ORYX.Core.SVG.Label({
          textElement: textElem,
          shapeId: this.id
        })
        label.x -= offsetX
        label.y -= offsetY
        this._labels.set(label.id, label)

        label.registerOnChange(this.layout.bind(this))

        // Only apply fitting on form-components
        if (this._stencil.id().indexOf(ORYX.CONFIG.FORM_ELEMENT_ID_PREFIX) == 0) {
          label.registerOnChange(this.fitToLabels.bind(this))
        }
      }.bind(this)
    )
  },

  fitToLabels: function() {
    var y = 0

    this.getLabels().each(function(label) {
      var lr = label.getY() + label.getHeight()
      if (lr > y) {
        y = lr
      }
    })

    var bounds = this.bounds
    var boundsChanged = false

    if (this.minimumSize) {
      // Check if y-value exceeds the min-value. If not, stick to this value.
      var minHeight = this.minimumSize.height
      if (y < minHeight && bounds.height() > minHeight && minHeight > this.forcedHeight) {
        bounds.set(bounds.upperLeft().x, bounds.upperLeft().y, bounds.lowerRight().x, bounds.upperLeft().y + minHeight)
        boundsChanged = true
      } else if (y > minHeight && bounds.height() != y && y > this.forcedHeight) {
        bounds.set(bounds.upperLeft().x, bounds.upperLeft().y, bounds.lowerRight().x, bounds.upperLeft().y + y)
        boundsChanged = true
      } else if (bounds.height() > this.forcedHeight && this.forcedHeight > 0) {
        bounds.set(
          bounds.upperLeft().x,
          bounds.upperLeft().y,
          bounds.lowerRight().x,
          bounds.upperLeft().y + this.forcedHeight
        )
        boundsChanged = true
      }
    }

    if (boundsChanged) {
      // Force facade to re-layout since bounds are changed AFTER layout has been performed
      if (this.facade.getCanvas() != null) {
        this.facade.getCanvas().update()
      }

      // Re-select if needed to force the select
      if (this.facade.getSelection().member(this)) {
        var selectedNow = this.facade.getSelection()
        this.facade.setSelection([])
        this.facade.setSelection(selectedNow)
      }
    }
  },

  /**
   * Override the Method, that a docker is not shown
   *
   */
  createDocker: function() {
    var docker = new ORYX.Core.Controls.Docker({ eventHandlerCallback: this.eventHandlerCallback })
    docker.bounds.registerCallback(this._dockerChangedCallback)

    this.dockers.push(docker)
    docker.parent = this
    docker.bounds.registerCallback(this._changedCallback)

    return docker
  },

  toString: function() {
    return this._stencil.title() + ' ' + this.id
  }
}
ORYX.Core.Node = ORYX.Core.Shape.extend(ORYX.Core.Node)
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

NAMESPACE_SVG = 'http://www.w3.org/2000/svg'
NAMESPACE_ORYX = 'http://www.b3mn.org/oryx'

/**
 * Init namespaces
 */
if (!ORYX) {
  var ORYX = {}
}
if (!ORYX.Core) {
  ORYX.Core = {}
}

/**
 * @classDescription Abstract base class for all connections.
 * @extends {ORYX.Core.Shape}
 * @param options {Object}
 *
 * TODO da die verschiebung der Edge nicht ueber eine
 *  translation gemacht wird, die sich auch auf alle kind UIObjects auswirkt,
 *  muessen die kinder hier beim verschieben speziell betrachtet werden.
 *  Das sollte ueberarbeitet werden.
 *
 */
ORYX.Core.Edge = {
  /**
   * Constructor
   * @param {Object} options
   * @param {Stencil} stencil
   */
  construct: function(options, stencil, facade) {
    arguments.callee.$.construct.apply(this, arguments)

    this.isMovable = true
    this.isSelectable = true

    this._dockerUpdated = false

    this._markers = new Hash() //a hash map of SVGMarker objects where keys are the marker ids
    this._paths = []
    this._interactionPaths = []
    this._dockersByPath = new Hash()
    this._markersByPath = new Hash()

    /* Data structures to store positioning information of attached child nodes */

    this.attachedNodePositionData = new Hash()

    //TODO was muss hier initial erzeugt werden?
    var stencilNode = this.node.childNodes[0].childNodes[0]
    stencilNode = ORYX.Editor.graft('http://www.w3.org/2000/svg', stencilNode, [
      'g',
      {
        'pointer-events': 'painted'
      }
    ])

    //Add to the EventHandler
    this.addEventHandlers(stencilNode.parentNode)

    this._oldBounds = this.bounds.clone()

    //load stencil
    this._init(this._stencil.view())

    if (stencil instanceof Array) {
      this.deserialize(stencil)
    }
  },

  _update: function(force) {
    if (this._dockerUpdated || this.isChanged || force) {
      this.dockers.invoke('update')

      if (false && (this.bounds.width() === 0 || this.bounds.height() === 0)) {
        var width = this.bounds.width()
        var height = this.bounds.height()
        this.bounds.extend({
          x: width === 0 ? 2 : 0,
          y: height === 0 ? 2 : 0
        })
        this.bounds.moveBy({
          x: width === 0 ? -1 : 0,
          y: height === 0 ? -1 : 0
        })
      }

      // TODO: Bounds muss abhaengig des Eltern-Shapes gesetzt werden
      var upL = this.bounds.upperLeft()
      var oldUpL = this._oldBounds.upperLeft()
      var oldWidth = this._oldBounds.width() === 0 ? this.bounds.width() : this._oldBounds.width()
      var oldHeight = this._oldBounds.height() === 0 ? this.bounds.height() : this._oldBounds.height()
      var diffX = upL.x - oldUpL.x
      var diffY = upL.y - oldUpL.y
      var diffWidth = this.bounds.width() / oldWidth || 1
      var diffHeight = this.bounds.height() / oldHeight || 1

      this.dockers.each(
        function(docker) {
          // Unregister on BoundsChangedCallback
          docker.bounds.unregisterCallback(this._dockerChangedCallback)

          // If there is any changes at the edge and is there is not an DockersUpdate
          // set the new bounds to the docker
          if (!this._dockerUpdated) {
            docker.bounds.moveBy(diffX, diffY)

            if (diffWidth !== 1 || diffHeight !== 1) {
              var relX = docker.bounds.upperLeft().x - upL.x
              var relY = docker.bounds.upperLeft().y - upL.y

              docker.bounds.moveTo(upL.x + relX * diffWidth, upL.y + relY * diffHeight)
            }
          }
          // Do Docker update and register on DockersBoundChange
          docker.update()
          docker.bounds.registerCallback(this._dockerChangedCallback)
        }.bind(this)
      )

      if (this._dockerUpdated) {
        var a = this.dockers.first().bounds.center()
        var b = this.dockers.first().bounds.center()

        this.dockers.each(
          function(docker) {
            var center = docker.bounds.center()
            a.x = Math.min(a.x, center.x)
            a.y = Math.min(a.y, center.y)
            b.x = Math.max(b.x, center.x)
            b.y = Math.max(b.y, center.y)
          }.bind(this)
        )

        //set the bounds of the the association
        this.bounds.set(Object.clone(a), Object.clone(b))
      }

      upL = this.bounds.upperLeft()
      oldUpL = this._oldBounds.upperLeft()
      diffWidth = this.bounds.width() / (oldWidth || this.bounds.width())
      diffHeight = this.bounds.height() / (oldHeight || this.bounds.height())
      diffX = upL.x - oldUpL.x
      diffY = upL.y - oldUpL.y

      //reposition labels
      this.getLabels().each(
        function(label) {
          if (label.getReferencePoint()) {
            var ref = label.getReferencePoint()
            var from = ref.segment.from,
              to = ref.segment.to
            if (!from || !from.parent || !to || !to.parent) {
              return
            }

            var fromPosition = from.bounds.center(),
              toPosition = to.bounds.center()

            if (
              fromPosition.x === ref.segment.fromPosition.x &&
              fromPosition.y === ref.segment.fromPosition.y &&
              toPosition.x === ref.segment.toPosition.x &&
              toPosition.y === ref.segment.toPosition.y &&
              !ref.dirty
            ) {
              return
            }

            if (!this.parent.initializingShapes) {
              var oldDistance = ORYX.Core.Math.getDistanceBetweenTwoPoints(
                ref.segment.fromPosition,
                ref.segment.toPosition,
                ref.intersection
              )
              var newIntersection = ORYX.Core.Math.getPointBetweenTwoPoints(
                fromPosition,
                toPosition,
                isNaN(oldDistance) ? 0.5 : oldDistance
              )

              /**
               * Set position
               */
              // Get the orthogonal identity vector of the current segment
              var oiv = ORYX.Core.Math.getOrthogonalIdentityVector(fromPosition, toPosition)
              var isHor = Math.abs(oiv.y) === 1,
                isVer = Math.abs(oiv.x) === 1
              oiv.x *= ref.distance
              oiv.y *= ref.distance // vector * distance
              oiv.x += newIntersection.x
              oiv.y += newIntersection.y // vector + the intersection point
              var mx =
                isHor && ref.orientation && (ref.iorientation || ref.orientation).endsWith('r') ? -label.getWidth() : 0
              var my =
                isVer && ref.orientation && (ref.iorientation || ref.orientation).startsWith('l')
                  ? -label.getHeight() + 2
                  : 0
              label.setX(oiv.x + mx)
              label.setY(oiv.y + my)

              // Update the reference point
              this.updateReferencePointOfLabel(label, newIntersection, from, to)
            } else {
              var oiv = ORYX.Core.Math.getOrthogonalIdentityVector(fromPosition, toPosition)
              oiv.x *= ref.distance
              oiv.y *= ref.distance // vector * distance
              oiv.x += ref.intersection.x
              oiv.y += ref.intersection.y // vector + the intersection point
              label.setX(oiv.x)
              label.setY(oiv.y)
              ref.segment.fromPosition = fromPosition
              ref.segment.toPosition = toPosition
            }

            return
          }

          // Update label position if no reference point is set
          if (label.position && !this.parent.initializingShapes) {
            var x = label.position.x + diffX * (diffWidth || 1)
            if (x > this.bounds.lowerRight().x) {
              x += this.bounds.width() - this.bounds.width() / (diffWidth || 1)
            }

            var y = label.position.y + diffY * (diffHeight || 1)
            if (y > this.bounds.lowerRight().y) {
              y += this.bounds.height() - this.bounds.height() / (diffHeight || 1)
            }
            label.setX(x)
            label.setY(y)
            return
          }

          switch (label.getEdgePosition()) {
            case 'starttop':
              var angle = this._getAngle(this.dockers[0], this.dockers[1])
              var pos = this.dockers.first().bounds.center()

              if (angle <= 90 || angle > 270) {
                label.horizontalAlign('left')
                label.verticalAlign('bottom')
                label.x = pos.x + label.getOffsetTop()
                label.y = pos.y - label.getOffsetTop()
                label.rotate(360 - angle, pos)
              } else {
                label.horizontalAlign('right')
                label.verticalAlign('bottom')
                label.x = pos.x - label.getOffsetTop()
                label.y = pos.y - label.getOffsetTop()
                label.rotate(180 - angle, pos)
              }

              break

            case 'startmiddle':
              var angle = this._getAngle(this.dockers[0], this.dockers[1])
              var pos = this.dockers.first().bounds.center()

              if (angle <= 90 || angle > 270) {
                label.horizontalAlign('left')
                label.verticalAlign('bottom')
                label.x = pos.x + 2
                label.y = pos.y + 4
                label.rotate(360 - angle, pos)
              } else {
                label.horizontalAlign('right')
                label.verticalAlign('bottom')
                label.x = pos.x + 1
                label.y = pos.y + 4
                label.rotate(180 - angle, pos)
              }

              break

            case 'startbottom':
              var angle = this._getAngle(this.dockers[0], this.dockers[1])
              var pos = this.dockers.first().bounds.center()

              if (angle <= 90 || angle > 270) {
                label.horizontalAlign('left')
                label.verticalAlign('top')
                label.x = pos.x + label.getOffsetBottom()
                label.y = pos.y + label.getOffsetBottom()
                label.rotate(360 - angle, pos)
              } else {
                label.horizontalAlign('right')
                label.verticalAlign('top')
                label.x = pos.x - label.getOffsetBottom()
                label.y = pos.y + label.getOffsetBottom()
                label.rotate(180 - angle, pos)
              }

              break
            case 'midtop':
              var numOfDockers = this.dockers.length
              if (numOfDockers % 2 == 0) {
                var angle = this._getAngle(this.dockers[numOfDockers / 2 - 1], this.dockers[numOfDockers / 2])
                var pos1 = this.dockers[numOfDockers / 2 - 1].bounds.center()
                var pos2 = this.dockers[numOfDockers / 2].bounds.center()
                var pos = { x: (pos1.x + pos2.x) / 2.0, y: (pos1.y + pos2.y) / 2.0 }

                label.horizontalAlign('center')
                label.verticalAlign('bottom')
                label.x = pos.x
                label.y = pos.y - label.getOffsetTop()

                if (angle <= 90 || angle > 270) {
                  label.rotate(360 - angle, pos)
                } else {
                  label.rotate(180 - angle, pos)
                }
              } else {
                var index = parseInt(numOfDockers / 2)
                var angle = this._getAngle(this.dockers[index], this.dockers[index + 1])
                var pos = this.dockers[index].bounds.center()

                if (angle <= 90 || angle > 270) {
                  label.horizontalAlign('left')
                  label.verticalAlign('bottom')
                  label.x = pos.x + label.getOffsetTop()
                  label.y = pos.y - label.getOffsetTop()
                  label.rotate(360 - angle, pos)
                } else {
                  label.horizontalAlign('right')
                  label.verticalAlign('bottom')
                  label.x = pos.x - label.getOffsetTop()
                  label.y = pos.y - label.getOffsetTop()
                  label.rotate(180 - angle, pos)
                }
              }

              break
            case 'midbottom':
              var numOfDockers = this.dockers.length
              if (numOfDockers % 2 == 0) {
                var angle = this._getAngle(this.dockers[numOfDockers / 2 - 1], this.dockers[numOfDockers / 2])
                var pos1 = this.dockers[numOfDockers / 2 - 1].bounds.center()
                var pos2 = this.dockers[numOfDockers / 2].bounds.center()
                var pos = { x: (pos1.x + pos2.x) / 2.0, y: (pos1.y + pos2.y) / 2.0 }

                label.horizontalAlign('center')
                label.verticalAlign('top')
                label.x = pos.x
                label.y = pos.y + label.getOffsetTop()

                if (angle <= 90 || angle > 270) {
                  label.rotate(360 - angle, pos)
                } else {
                  label.rotate(180 - angle, pos)
                }
              } else {
                var index = parseInt(numOfDockers / 2)
                var angle = this._getAngle(this.dockers[index], this.dockers[index + 1])
                var pos = this.dockers[index].bounds.center()

                if (angle <= 90 || angle > 270) {
                  label.horizontalAlign('left')
                  label.verticalAlign('top')
                  label.x = pos.x + label.getOffsetBottom()
                  label.y = pos.y + label.getOffsetBottom()
                  label.rotate(360 - angle, pos)
                } else {
                  label.horizontalAlign('right')
                  label.verticalAlign('top')
                  label.x = pos.x - label.getOffsetBottom()
                  label.y = pos.y + label.getOffsetBottom()
                  label.rotate(180 - angle, pos)
                }
              }

              break
            case 'endtop':
              var length = this.dockers.length
              var angle = this._getAngle(this.dockers[length - 2], this.dockers[length - 1])
              var pos = this.dockers.last().bounds.center()

              if (angle <= 90 || angle > 270) {
                label.horizontalAlign('right')
                label.verticalAlign('bottom')
                label.x = pos.x - label.getOffsetTop()
                label.y = pos.y - label.getOffsetTop()
                label.rotate(360 - angle, pos)
              } else {
                label.horizontalAlign('left')
                label.verticalAlign('bottom')
                label.x = pos.x + label.getOffsetTop()
                label.y = pos.y - label.getOffsetTop()
                label.rotate(180 - angle, pos)
              }

              break
            case 'endbottom':
              var length = this.dockers.length
              var angle = this._getAngle(this.dockers[length - 2], this.dockers[length - 1])
              var pos = this.dockers.last().bounds.center()

              if (angle <= 90 || angle > 270) {
                label.horizontalAlign('right')
                label.verticalAlign('top')
                label.x = pos.x - label.getOffsetBottom()
                label.y = pos.y + label.getOffsetBottom()
                label.rotate(360 - angle, pos)
              } else {
                label.horizontalAlign('left')
                label.verticalAlign('top')
                label.x = pos.x + label.getOffsetBottom()
                label.y = pos.y + label.getOffsetBottom()
                label.rotate(180 - angle, pos)
              }

              break
          }
        }.bind(this)
      )

      this.children.each(
        function(value) {
          if (value instanceof ORYX.Core.Node) {
            this.calculatePositionOfAttachedChildNode.call(this, value)
          }
        }.bind(this)
      )

      this.refreshAttachedNodes()
      this.refresh()

      this.isChanged = false
      this._dockerUpdated = false

      this._oldBounds = this.bounds.clone()
    }

    // IE10 specific fix, start and end-markes get left behind when moving path
    var userAgent = navigator.userAgent
    if (
      navigator.appVersion.indexOf('MSIE 10') !== -1 ||
      (userAgent.indexOf('Trident') !== -1 && userAgent.indexOf('rv:11') !== -1)
    ) {
      this.node.parentNode.insertBefore(this.node, this.node)
    }
  },

  /**
   *  Moves a point to the upperLeft of a node's bounds.
   *
   *  @param {point} point
   *  	The point to move
   *  @param {ORYX.Core.Bounds} bounds
   *  	The Bounds of the related noe
   */
  movePointToUpperLeftOfNode: function(point, bounds) {
    point.x -= bounds.width() / 2
    point.y -= bounds.height() / 2
  },

  /**
   * Refreshes the visual representation of edge's attached nodes.
   */

  refreshAttachedNodes: function() {
    this.attachedNodePositionData.values().each(
      function(nodeData) {
        var startPoint = nodeData.segment.docker1.bounds.center()
        var endPoint = nodeData.segment.docker2.bounds.center()
        this.relativizePoint(startPoint)
        this.relativizePoint(endPoint)

        var newNodePosition = new Object()

        /* Calculate new x-coordinate */
        newNodePosition.x = startPoint.x + nodeData.relativDistanceFromDocker1 * (endPoint.x - startPoint.x)

        /* Calculate new y-coordinate */
        newNodePosition.y = startPoint.y + nodeData.relativDistanceFromDocker1 * (endPoint.y - startPoint.y)

        /* Convert new position to the upper left of the node */
        this.movePointToUpperLeftOfNode(newNodePosition, nodeData.node.bounds)

        /* Move node to its new position */
        nodeData.node.bounds.moveTo(newNodePosition)
        nodeData.node._update()
      }.bind(this)
    )
  },

  /**
   * Calculates the position of an edge's child node. The node is placed on
   * the path of the edge.
   *
   * @param {node}
   * 		The node to calculate the new position
   * @return {Point}
   * 		The calculated upper left point of the node's shape.
   */
  calculatePositionOfAttachedChildNode: function(node) {
    /* Initialize position */
    var position = new Object()
    position.x = 0
    position.y = 0

    /* Case: Node was just added */
    if (!this.attachedNodePositionData.get(node.getId())) {
      this.attachedNodePositionData.set(node.getId(), new Object())
      this.attachedNodePositionData.get(node.getId()).relativDistanceFromDocker1 = 0
      this.attachedNodePositionData.get(node.getId()).node = node
      this.attachedNodePositionData.get(node.getId()).segment = new Object()
      this.findEdgeSegmentForNode(node)
    } else if (node.isChanged) {
      this.findEdgeSegmentForNode(node)
    }
  },

  /**
   * Finds the appropriate edge segement for a node.
   * The segment is choosen, which has the smallest distance to the node.
   *
   * @param {ORYX.Core.Node} node
   * 		The concerning node
   */
  findEdgeSegmentForNode: function(node) {
    var length = this.dockers.length
    var smallestDistance = undefined

    for (i = 1; i < length; i++) {
      var lineP1 = this.dockers[i - 1].bounds.center()
      var lineP2 = this.dockers[i].bounds.center()
      this.relativizePoint(lineP1)
      this.relativizePoint(lineP2)

      var nodeCenterPoint = node.bounds.center()
      var distance = ORYX.Core.Math.distancePointLinie(lineP1, lineP2, nodeCenterPoint, true)

      if (
        (distance || distance == 0) &&
        ((!smallestDistance && smallestDistance != 0) || distance < smallestDistance)
      ) {
        smallestDistance = distance

        this.attachedNodePositionData.get(node.getId()).segment.docker1 = this.dockers[i - 1]
        this.attachedNodePositionData.get(node.getId()).segment.docker2 = this.dockers[i]
      }

      /* Either the distance does not match the segment or the distance
       * between docker1 and docker2 is 0
       *
       * In this case choose the nearest docker as attaching point.
       *
       */
      if (!distance && !smallestDistance && smallestDistance != 0) {
        var distanceCenterToLineOne = ORYX.Core.Math.getDistancePointToPoint(nodeCenterPoint, lineP1)
        var distanceCenterToLineTwo = ORYX.Core.Math.getDistancePointToPoint(nodeCenterPoint, lineP2)

        if (distanceCenterToLineOne < distanceCenterToLineTwo) {
          this.attachedNodePositionData.get(node.getId()).relativDistanceFromDocker1 = 0
        } else {
          this.attachedNodePositionData.get(node.getId()).relativDistanceFromDocker1 = 1
        }
        this.attachedNodePositionData.get(node.getId()).segment.docker1 = this.dockers[i - 1]
        this.attachedNodePositionData.get(node.getId()).segment.docker2 = this.dockers[i]
      }
    }

    /* Calculate position on edge segment for the node */
    if (smallestDistance || smallestDistance == 0) {
      this.attachedNodePositionData.get(node.getId()).relativDistanceFromDocker1 = this.getLineParameterForPosition(
        this.attachedNodePositionData.get(node.getId()).segment.docker1,
        this.attachedNodePositionData.get(node.getId()).segment.docker2,
        node
      )
    }
  },

  /**
   *
   * @param {ORYX.Core.Node|Object} node or position
   * @return {Object} An object with the following attribute: {ORYX.Core.Docker} fromDocker, {ORYX.Core.Docker} toDocker, {X/Y} position, {int} distance
   */
  findSegment: function(node) {
    var length = this.dockers.length
    var result

    var nodeCenterPoint = node instanceof ORYX.Core.UIObject ? node.bounds.center() : node

    for (i = 1; i < length; i++) {
      var lineP1 = this.dockers[i - 1].bounds.center()
      var lineP2 = this.dockers[i].bounds.center()

      var distance = ORYX.Core.Math.distancePointLinie(lineP1, lineP2, nodeCenterPoint, true)

      if (typeof distance == 'number' && (result === undefined || distance < result.distance)) {
        result = {
          distance: distance,
          fromDocker: this.dockers[i - 1],
          toDocker: this.dockers[i]
        }
      }
    }
    return result
  },

  /**
   * Returns the value of the scalar to determine the position of the node on
   * line defined by docker1 and docker2.
   *
   * @param {point} docker1
   * 		The docker that defines the start of the line segment
   * @param {point} docker2
   * 		The docker that defines the end of the line segment
   * @param {ORYX.Core.Node} node
   * 		The concerning node
   *
   * @return {float} positionParameter
   * 		The scalar value to determine the position on the line
   */
  getLineParameterForPosition: function(docker1, docker2, node) {
    var dockerPoint1 = docker1.bounds.center()
    var dockerPoint2 = docker2.bounds.center()
    this.relativizePoint(dockerPoint1)
    this.relativizePoint(dockerPoint2)

    var intersectionPoint = ORYX.Core.Math.getPointOfIntersectionPointLine(
      dockerPoint1,
      dockerPoint2,
      node.bounds.center(),
      true
    )
    if (!intersectionPoint) {
      return 0
    }

    var relativeDistance =
      ORYX.Core.Math.getDistancePointToPoint(intersectionPoint, dockerPoint1) /
      ORYX.Core.Math.getDistancePointToPoint(dockerPoint1, dockerPoint2)

    return relativeDistance
  },
  /**
   * Makes point relative to the upper left of the edge's bound.
   *
   * @param {point} point
   * 		The point to relativize
   */
  relativizePoint: function(point) {
    point.x -= this.bounds.upperLeft().x
    point.y -= this.bounds.upperLeft().y
  },

  /**
   * Move the first and last docker and calls the refresh method.
   * Attention: This does not calculates intersection point between the
   * edge and the bounded nodes. This only works if only the nodes are
   * moves.
   *
   */
  optimizedUpdate: function() {
    var updateDocker = function(docker) {
      if (!docker._dockedShape || !docker._dockedShapeBounds) return
      var off = {
        x: docker._dockedShape.bounds.a.x - docker._dockedShapeBounds.a.x,
        y: docker._dockedShape.bounds.a.y - docker._dockedShapeBounds.a.y
      }
      docker.bounds.moveBy(off)
      docker._dockedShapeBounds.moveBy(off)
    }

    updateDocker(this.dockers.first())
    updateDocker(this.dockers.last())

    this.refresh()
  },

  refresh: function() {
    //call base class refresh method
    arguments.callee.$.refresh.apply(this, arguments)

    //TODO consider points for marker mids
    var lastPoint
    this._paths.each(
      function(path, index) {
        var dockers = this._dockersByPath.get(path.id)
        var c = undefined
        var d = undefined
        if (lastPoint) {
          d = 'M' + lastPoint.x + ' ' + lastPoint.y
        } else {
          c = dockers[0].bounds.center()
          lastPoint = c

          d = 'M' + c.x + ' ' + c.y
        }

        for (var i = 1; i < dockers.length; i++) {
          // for each docker, draw a line to the center
          c = dockers[i].bounds.center()
          d = d + 'L' + c.x + ' ' + c.y + ' '
          lastPoint = c
        }

        path.setAttributeNS(null, 'd', d)
        this._interactionPaths[index].setAttributeNS(null, 'd', d)
      }.bind(this)
    )

    /* move child shapes of an edge */
    if (this.getChildNodes().length > 0) {
      var x = this.bounds.upperLeft().x
      var y = this.bounds.upperLeft().y

      this.node.firstChild.childNodes[1].setAttributeNS(null, 'transform', 'translate(' + x + ', ' + y + ')')
    }
  },

  /**
   * Calculate the Border Intersection Point between two points
   * @param {PointA}
   * @param {PointB}
   */
  getIntersectionPoint: function() {
    var length = Math.floor(this.dockers.length / 2)

    return ORYX.Core.Math.midPoint(this.dockers[length - 1].bounds.center(), this.dockers[length].bounds.center())
  },

  /**
   * Returns TRUE if the bounds is over the edge
   * @param {Bounds}
   *
   */
  isBoundsIncluded: function(bounds) {
    var dockers = this.dockers,
      size = dockers.length
    return dockers.any(function(docker, i) {
      if (i == size - 1) {
        return false
      }
      var a = docker.bounds.center()
      var b = dockers[i + 1].bounds.center()

      return ORYX.Core.Math.isRectOverLine(a.x, a.y, b.x, b.y, bounds.a.x, bounds.a.y, bounds.b.x, bounds.b.y)
    })
  },

  /**
   * Calculate if the point is inside the Shape
   * @param {PointX}
   * @param {PointY}
   */
  isPointIncluded: function(pointX, pointY) {
    var isbetweenAB = this.absoluteBounds().isIncluded(pointX, pointY, ORYX.CONFIG.OFFSET_EDGE_BOUNDS)

    var isPointIncluded = undefined

    if (isbetweenAB && this.dockers.length > 0) {
      var i = 0
      var point1, point2

      do {
        point1 = this.dockers[i].bounds.center()
        point2 = this.dockers[++i].bounds.center()

        isPointIncluded = ORYX.Core.Math.isPointInLine(
          pointX,
          pointY,
          point1.x,
          point1.y,
          point2.x,
          point2.y,
          ORYX.CONFIG.OFFSET_EDGE_BOUNDS
        )
      } while (!isPointIncluded && i < this.dockers.length - 1)
    }

    return isPointIncluded
  },

  /**
   * Calculate if the point is over an special offset area
   * @param {Point}
   */
  isPointOverOffset: function() {
    return false
  },

  /**
   * Returns TRUE if the given node
   * is a child node of the shapes node
   * @param {Element} node
   * @return {Boolean}
   *
   */
  containsNode: function(node) {
    if (this._paths.include(node) || this._interactionPaths.include(node)) {
      return true
    }
    return false
  },

  /**
   * Returns the angle of the line between two dockers
   * (0 - 359.99999999)
   */
  _getAngle: function(docker1, docker2) {
    var p1 = docker1 instanceof ORYX.Core.Controls.Docker ? docker1.absoluteCenterXY() : docker1
    var p2 = docker2 instanceof ORYX.Core.Controls.Docker ? docker2.absoluteCenterXY() : docker2

    return ORYX.Core.Math.getAngle(p1, p2)
  },

  alignDockers: function() {
    this._update(true)

    var firstPoint = this.dockers.first().bounds.center()
    var lastPoint = this.dockers.last().bounds.center()

    var deltaX = lastPoint.x - firstPoint.x
    var deltaY = lastPoint.y - firstPoint.y

    var numOfDockers = this.dockers.length - 1

    this.dockers.each(
      function(docker, index) {
        var part = index / numOfDockers
        docker.bounds.unregisterCallback(this._dockerChangedCallback)
        docker.bounds.moveTo(firstPoint.x + part * deltaX, firstPoint.y + part * deltaY)
        docker.bounds.registerCallback(this._dockerChangedCallback)
      }.bind(this)
    )

    this._dockerChanged()
  },

  add: function(shape) {
    arguments.callee.$.add.apply(this, arguments)

    // If the new shape is a Docker which is not contained
    if (shape instanceof ORYX.Core.Controls.Docker && this.dockers.include(shape)) {
      // Add it to the dockers list ordered by paths
      var pathArray = this._dockersByPath.values()[0]
      if (pathArray) {
        pathArray.splice(this.dockers.indexOf(shape), 0, shape)
      }

      /* Perform nessary adjustments on the edge's child shapes */
      this.handleChildShapesAfterAddDocker(shape)
    }
  },

  /**
   * Performs nessary adjustments on the edge's child shapes.
   *
   * @param {ORYX.Core.Controls.Docker} docker
   * 		The added docker
   */
  handleChildShapesAfterAddDocker: function(docker) {
    /* Ensure type of Docker */
    if (!docker instanceof ORYX.Core.Controls.Docker) {
      return undefined
    }

    var index = this.dockers.indexOf(docker)
    if (!(0 < index && index < this.dockers.length - 1)) {
      /* Exception: Expect added docker between first and last node of the edge */
      return undefined
    }

    /* Get child nodes concerning the segment of the new docker */
    var startDocker = this.dockers[index - 1]
    var endDocker = this.dockers[index + 1]

    /* Adjust the position of edge's child nodes */
    var segmentElements = this.getAttachedNodePositionDataForSegment(startDocker, endDocker)

    var lengthSegmentPart1 = ORYX.Core.Math.getDistancePointToPoint(startDocker.bounds.center(), docker.bounds.center())
    var lengthSegmentPart2 = ORYX.Core.Math.getDistancePointToPoint(endDocker.bounds.center(), docker.bounds.center())

    if (!(lengthSegmentPart1 + lengthSegmentPart2)) {
      return
    }

    var relativDockerPosition = lengthSegmentPart1 / (lengthSegmentPart1 + lengthSegmentPart2)

    segmentElements.each(function(nodePositionData) {
      /* Assign child node to the new segment */
      if (nodePositionData.value.relativDistanceFromDocker1 < relativDockerPosition) {
        /* Case: before added Docker */
        nodePositionData.value.segment.docker2 = docker
        nodePositionData.value.relativDistanceFromDocker1 =
          nodePositionData.value.relativDistanceFromDocker1 / relativDockerPosition
      } else {
        /* Case: after added Docker */
        nodePositionData.value.segment.docker1 = docker
        var newFullDistance = 1 - relativDockerPosition
        var relativPartOfSegment = nodePositionData.value.relativDistanceFromDocker1 - relativDockerPosition

        nodePositionData.value.relativDistanceFromDocker1 = relativPartOfSegment / newFullDistance
      }
    })

    // Update all labels reference points
    this.getLabels().each(
      function(label) {
        var ref = label.getReferencePoint()
        if (!ref) {
          return
        }
        var index = this.dockers.indexOf(docker)
        if (index >= ref.segment.fromIndex && index <= ref.segment.toIndex) {
          var segment = this.findSegment(ref.intersection)
          if (!segment) {
            // Choose whether the first of the last segment
            segment.fromDocker =
              ref.segment.fromIndex >= this.dockers.length / 2 ? this.dockers[0] : this.dockers[this.dockers.length - 2]
            segment.toDocker = this.dockers[this.dockers.indexOf(from) + 1] // The next one if the to docker
          }

          var fromPosition = segment.fromDocker.bounds.center(),
            toPosition = segment.toDocker.bounds.center()

          var intersection = ORYX.Core.Math.getPointOfIntersectionPointLine(
            fromPosition, // P1 - Center of the first docker
            toPosition, // P2 - Center of the second docker
            ref.intersection, // P3 - Center of the label
            true
          )
          //var oldDistance = ORYX.Core.Math.getDistanceBetweenTwoPoints(ref.segment.fromPosition, ref.segment.toPosition, ref.intersection);
          //intersection = ORYX.Core.Math.getPointBetweenTwoPoints(fromPosition, toPosition, isNaN(oldDistance) ? 0.5 : (lengthOld*oldDistance)/lengthNew);

          // Update the reference point
          this.updateReferencePointOfLabel(label, intersection, segment.fromDocker, segment.toDocker, true)
        }
      }.bind(this)
    )

    /* Update attached nodes visual representation */
    this.refreshAttachedNodes()
  },

  /**
   *	Returns elements from {@link attachedNodePositiondata} that match the
   *  segement defined by startDocker and endDocker.
   *
   *  @param {ORYX.Core.Controls.Docker} startDocker
   *  	The docker defining the begin of the segment.
   *  @param {ORYX.Core.Controls.Docker} endDocker
   *  	The docker defining the begin of the segment.
   *
   *  @return {Hash} attachedNodePositionData
   *  	Child elements matching the segment
   */
  getAttachedNodePositionDataForSegment: function(startDocker, endDocker) {
    /* Ensure that the segment is defined correctly */
    if (!(startDocker instanceof ORYX.Core.Controls.Docker && endDocker instanceof ORYX.Core.Controls.Docker)) {
      return []
    }

    /* Get elements of the segment */
    var elementsOfSegment = this.attachedNodePositionData.findAll(function(nodePositionData) {
      return (
        nodePositionData.value.segment.docker1 === startDocker && nodePositionData.value.segment.docker2 === endDocker
      )
    })

    /* Return a Hash in each case */
    if (!elementsOfSegment) {
      return []
    }

    return elementsOfSegment
  },

  /**
   * Removes an edge's child shape
   */
  remove: function(shape) {
    arguments.callee.$.remove.apply(this, arguments)

    if (this.attachedNodePositionData.get(shape.getId())) {
      this.attachedNodePositionData.unset[shape.getId()]
    }

    /* Adjust child shapes if neccessary */
    if (shape instanceof ORYX.Core.Controls.Docker) {
      this.handleChildShapesAfterRemoveDocker(shape)
    }
  },

  updateReferencePointOfLabel: function(label, intersection, from, to, dirty) {
    if (!label.getReferencePoint() || !label.isVisible) {
      return
    }

    var ref = label.getReferencePoint()

    //
    if (ref.orientation && ref.orientation !== 'ce') {
      var angle = this._getAngle(from, to)
      if (ref.distance >= 0) {
        if (angle == 0) {
          label.horizontalAlign('left') //ref.orientation == "lr" ? "right" : "left");
          label.verticalAlign('bottom')
        } else if (angle > 0 && angle < 90) {
          label.horizontalAlign('right')
          label.verticalAlign('bottom')
        } else if (angle == 90) {
          label.horizontalAlign('right')
          label.verticalAlign('top') //ref.orientation == "lr" ? "bottom" : "top");
        } else if (angle > 90 && angle < 180) {
          label.horizontalAlign('right')
          label.verticalAlign('top')
        } else if (angle == 180) {
          label.horizontalAlign('left') //ref.orientation == "ur" ? "right" : "left");
          label.verticalAlign('top')
        } else if (angle > 180 && angle < 270) {
          label.horizontalAlign('left')
          label.verticalAlign('top')
        } else if (angle == 270) {
          label.horizontalAlign('left')
          label.verticalAlign('top') //ref.orientation == "ll" ? "bottom" : "top");
        } else if (angle > 270 && angle <= 360) {
          label.horizontalAlign('left')
          label.verticalAlign('bottom')
        }
      } else {
        if (angle == 0) {
          label.horizontalAlign('left') //ref.orientation == "ur" ? "right" : "left");
          label.verticalAlign('top')
        } else if (angle > 0 && angle < 90) {
          label.horizontalAlign('left')
          label.verticalAlign('top')
        } else if (angle == 90) {
          label.horizontalAlign('left')
          label.verticalAlign('top') //ref.orientation == "ll" ? "bottom" : "top");
        } else if (angle > 90 && angle < 180) {
          label.horizontalAlign('left')
          label.verticalAlign('bottom')
        } else if (angle == 180) {
          label.horizontalAlign('left') //ref.orientation == "lr" ? "right" : "left");
          label.verticalAlign('bottom')
        } else if (angle > 180 && angle < 270) {
          label.horizontalAlign('right')
          label.verticalAlign('bottom')
        } else if (angle == 270) {
          label.horizontalAlign('right')
          label.verticalAlign('top') //ref.orientation == "lr" ? "bottom" : "top");
        } else if (angle > 270 && angle <= 360) {
          label.horizontalAlign('right')
          label.verticalAlign('top')
        }
      }
      ref.iorientation = ref.iorientation || ref.orientation
      ref.orientation = (label.verticalAlign() == 'top' ? 'u' : 'l') + (label.horizontalAlign() == 'left' ? 'l' : 'r')
    }

    label.setReferencePoint(
      jQuery.extend(
        {},
        {
          intersection: intersection,
          segment: {
            from: from,
            fromIndex: this.dockers.indexOf(from),
            fromPosition: from.bounds.center(),
            to: to,
            toIndex: this.dockers.indexOf(to),
            toPosition: to.bounds.center()
          },
          dirty: dirty || false
        },
        ref
      )
    )
  },
  /**
   * 	Adjusts the child shapes of an edges after a docker was removed.
   *
   *  @param{ORYX.Core.Controls.Docker} docker
   *  	The removed docker.
   */
  handleChildShapesAfterRemoveDocker: function(docker) {
    /* Ensure docker type */
    if (!(docker instanceof ORYX.Core.Controls.Docker)) {
      return
    }

    this.attachedNodePositionData.each(
      function(nodePositionData) {
        if (nodePositionData.value.segment.docker1 === docker) {
          /* The new start of the segment is the predecessor of docker2. */
          var index = this.dockers.indexOf(nodePositionData.value.segment.docker2)
          if (index == -1) {
            return
          }
          nodePositionData.value.segment.docker1 = this.dockers[index - 1]
        } else if (nodePositionData.value.segment.docker2 === docker) {
          /* The new end of the segment is the successor of docker1. */
          var index = this.dockers.indexOf(nodePositionData.value.segment.docker1)
          if (index == -1) {
            return
          }
          nodePositionData.value.segment.docker2 = this.dockers[index + 1]
        }
      }.bind(this)
    )

    // Update all labels reference points
    this.getLabels().each(
      function(label) {
        var ref = label.getReferencePoint()
        if (!ref) {
          return
        }
        var from = ref.segment.from
        var to = ref.segment.to

        if (from !== docker && to !== docker) {
          return
        }

        var segment = this.findSegment(ref.intersection)
        if (!segment) {
          from = segment.fromDocker
          to = segment.toDocker
        } else {
          from = from === docker ? this.dockers[this.dockers.indexOf(to) - 1] : from
          to = this.dockers[this.dockers.indexOf(from) + 1]
        }

        var intersection = ORYX.Core.Math.getPointOfIntersectionPointLine(
          from.bounds.center(),
          to.bounds.center(),
          ref.intersection,
          true
        )
        // Update the reference point
        this.updateReferencePointOfLabel(label, intersection, from, to, true)
      }.bind(this)
    )

    /* Update attached nodes visual representation */
    this.refreshAttachedNodes()
  },

  /**
   *@deprecated Use the .createDocker() Method and set the point via the bounds
   */
  addDocker: function(position, exDocker) {
    var lastDocker
    var result
    this._dockersByPath.any(
      function(pair) {
        return pair.value.any(
          function(docker, index) {
            if (!lastDocker) {
              lastDocker = docker
              return false
            } else {
              var point1 = lastDocker.bounds.center()
              var point2 = docker.bounds.center()

              var additionalIEZoom = 1
              if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) {
                var ua = navigator.userAgent
                if (ua.indexOf('MSIE') >= 0) {
                  //IE 10 and below
                  var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100)
                  if (zoom !== 100) {
                    additionalIEZoom = zoom / 100
                  }
                }
              }

              if (additionalIEZoom !== 1) {
                position.x = position.x / additionalIEZoom
                position.y = position.y / additionalIEZoom
              }

              if (ORYX.Core.Math.isPointInLine(position.x, position.y, point1.x, point1.y, point2.x, point2.y, 10)) {
                var path = this._paths.find(function(path) {
                  return path.id === pair.key
                })
                if (path) {
                  var allowAttr = path.getAttributeNS(NAMESPACE_ORYX, 'allowDockers')
                  if (allowAttr && allowAttr.toLowerCase() === 'no') {
                    return true
                  }
                }

                var newDocker = exDocker ? exDocker : this.createDocker(this.dockers.indexOf(lastDocker) + 1, position)
                newDocker.bounds.centerMoveTo(position)
                if (exDocker) this.add(newDocker, this.dockers.indexOf(lastDocker) + 1)
                result = newDocker
                return true
              } else {
                lastDocker = docker
                return false
              }
            }
          }.bind(this)
        )
      }.bind(this)
    )
    return result
  },

  removeDocker: function(docker) {
    if (this.dockers.length > 2 && !(this.dockers.first() === docker)) {
      this._dockersByPath.any(
        function(pair) {
          if (pair.value.member(docker)) {
            if (docker === pair.value.last()) {
              return true
            } else {
              this.remove(docker)
              this._dockersByPath.set(pair.key, pair.value.without(docker))
              this.isChanged = true
              this._dockerChanged()
              return true
            }
          }
          return false
        }.bind(this)
      )
    }
  },

  /**
   * Removes all dockers from the edge which are on
   * the line between two dockers
   * @return {Object} Removed dockers in an indicied array
   * (key is the removed position of the docker, value is docker themselve)
   */
  removeUnusedDockers: function() {
    var marked = new Hash()

    this.dockers.each(
      function(docker, i) {
        if (i == 0 || i == this.dockers.length - 1) {
          return
        }
        var previous = this.dockers[i - 1]

        /* Do not consider already removed dockers */
        if (marked.values().indexOf(previous) != -1 && this.dockers[i - 2]) {
          previous = this.dockers[i - 2]
        }
        var next = this.dockers[i + 1]

        var cp =
          previous.getDockedShape() && previous.referencePoint
            ? previous.getAbsoluteReferencePoint()
            : previous.bounds.center()
        var cn = next.getDockedShape() && next.referencePoint ? next.getAbsoluteReferencePoint() : next.bounds.center()
        var cd = docker.bounds.center()

        if (ORYX.Core.Math.isPointInLine(cd.x, cd.y, cp.x, cp.y, cn.x, cn.y, 1)) {
          marked.set(i, docker)
        }
      }.bind(this)
    )

    marked.each(
      function(docker) {
        this.removeDocker(docker.value)
      }.bind(this)
    )

    if (marked.values().length > 0) {
      this._update(true)
    }

    return marked
  },

  /**
   * Initializes the Edge after loading the SVG representation of the edge.
   * @param {SVGDocument} svgDocument
   */
  _init: function(svgDocument) {
    arguments.callee.$._init.apply(this, arguments)

    var minPointX, minPointY, maxPointX, maxPointY

    //init markers
    var defs = svgDocument.getElementsByTagNameNS(NAMESPACE_SVG, 'defs')
    if (defs.length > 0) {
      defs = defs[0]
      var markerElements = $A(defs.getElementsByTagNameNS(NAMESPACE_SVG, 'marker'))
      var marker
      var me = this
      markerElements.each(function(markerElement) {
        try {
          marker = new ORYX.Core.SVG.SVGMarker(markerElement.cloneNode(true))
          me._markers.set(marker.id, marker)
          var textElements = $A(marker.element.getElementsByTagNameNS(NAMESPACE_SVG, 'text'))
          var label
          textElements.each(function(textElement) {
            label = new ORYX.Core.SVG.Label({
              textElement: textElement,
              shapeId: this.id
            })
            me._labels.set(label.id, label)
          })
        } catch (e) {}
      })
    }

    var gs = svgDocument.getElementsByTagNameNS(NAMESPACE_SVG, 'g')
    if (gs.length <= 0) {
      throw 'Edge: No g element found.'
    }
    var g = gs[0]

    g.setAttributeNS(null, 'id', null)

    var isFirst = true

    $A(g.childNodes).each(
      function(path, index) {
        if (ORYX.Editor.checkClassType(path, SVGPathElement)) {
          path = path.cloneNode(false)

          var pathId = this.id + '_' + index
          path.setAttributeNS(null, 'id', pathId)
          this._paths.push(path)

          //check, if markers are set and update the id
          var markersByThisPath = []
          var markerUrl = path.getAttributeNS(null, 'marker-start')

          if (markerUrl && markerUrl !== '') {
            markerUrl = markerUrl.strip()
            markerUrl = markerUrl.replace(/^url\(#/, '')

            var markerStartId = this.getValidMarkerId(markerUrl)
            path.setAttributeNS(null, 'marker-start', 'url(#' + markerStartId + ')')

            markersByThisPath.push(this._markers.get(markerStartId))
          }

          markerUrl = path.getAttributeNS(null, 'marker-mid')

          if (markerUrl && markerUrl !== '') {
            markerUrl = markerUrl.strip()
            markerUrl = markerUrl.replace(/^url\(#/, '')
            var markerMidId = this.getValidMarkerId(markerUrl)
            path.setAttributeNS(null, 'marker-mid', 'url(#' + markerMidId + ')')

            markersByThisPath.push(this._markers.get(markerMidId))
          }

          markerUrl = path.getAttributeNS(null, 'marker-end')

          if (markerUrl && markerUrl !== '') {
            markerUrl = markerUrl.strip()

            var markerEndId = this.getValidMarkerId(markerUrl)
            path.setAttributeNS(null, 'marker-end', 'url(#' + markerEndId + ')')

            markersByThisPath.push(this._markers.get(markerEndId))
          }

          this._markersByPath[pathId] = markersByThisPath

          //init dockers
          var parser = new PathParser()
          var handler = new ORYX.Core.SVG.PointsPathHandler()
          parser.setHandler(handler)
          parser.parsePath(path)

          if (handler.points.length < 4) {
            throw 'Edge: Path has to have two or more points specified.'
          }

          this._dockersByPath.set(pathId, [])

          for (var i = 0; i < handler.points.length; i += 2) {
            //handler.points.each((function(point, pIndex){
            var x = handler.points[i]
            var y = handler.points[i + 1]
            if (isFirst || i > 0) {
              var docker = new ORYX.Core.Controls.Docker({
                eventHandlerCallback: this.eventHandlerCallback
              })
              docker.bounds.centerMoveTo(x, y)
              docker.bounds.registerCallback(this._dockerChangedCallback)
              this.add(docker, this.dockers.length)

              //this._dockersByPath[pathId].push(docker);

              //calculate minPoint and maxPoint
              if (minPointX) {
                minPointX = Math.min(x, minPointX)
                minPointY = Math.min(y, minPointY)
              } else {
                minPointX = x
                minPointY = y
              }

              if (maxPointX) {
                maxPointX = Math.max(x, maxPointX)
                maxPointY = Math.max(y, maxPointY)
              } else {
                maxPointX = x
                maxPointY = y
              }
            }
            //}).bind(this));
          }
          isFirst = false
        }
      }.bind(this)
    )

    this.bounds.set(minPointX, minPointY, maxPointX, maxPointY)

    if (false && (this.bounds.width() === 0 || this.bounds.height() === 0)) {
      var width = this.bounds.width()
      var height = this.bounds.height()

      this.bounds.extend({
        x: width === 0 ? 2 : 0,
        y: height === 0 ? 2 : 0
      })

      this.bounds.moveBy({
        x: width === 0 ? -1 : 0,
        y: height === 0 ? -1 : 0
      })
    }

    this._oldBounds = this.bounds.clone()

    //add paths to this.node
    this._paths.reverse()
    var paths = []
    this._paths.each(
      function(path) {
        paths.push(this.node.childNodes[0].childNodes[0].childNodes[0].appendChild(path))
      }.bind(this)
    )

    this._paths = paths

    //init interaction path
    this._paths.each(
      function(path) {
        var iPath = path.cloneNode(false)
        iPath.setAttributeNS(null, 'id', undefined)
        iPath.setAttributeNS(null, 'stroke-width', 10)
        iPath.setAttributeNS(null, 'visibility', 'hidden')
        iPath.setAttributeNS(null, 'stroke-dasharray', null)
        iPath.setAttributeNS(null, 'stroke', 'black')
        iPath.setAttributeNS(null, 'fill', 'none')
        iPath.setAttributeNS(null, 'title', this.getStencil().title())
        this._interactionPaths.push(this.node.childNodes[0].childNodes[0].childNodes[0].appendChild(iPath))
      }.bind(this)
    )

    this._paths.reverse()
    this._interactionPaths.reverse()

    /**initialize labels*/
    var textElems = svgDocument.getElementsByTagNameNS(ORYX.CONFIG.NAMESPACE_SVG, 'text')

    $A(textElems).each(
      function(textElem) {
        var label = new ORYX.Core.SVG.Label({
          textElement: textElem,
          shapeId: this.id
        })
        this.node.childNodes[0].childNodes[0].appendChild(label.node)
        this._labels.set(label.id, label)

        label.registerOnChange(this.layout.bind(this))
      }.bind(this)
    )

    this.propertiesChanged.each(function(pair) {
      pair.value = true
    })

    //if(this.dockers.length == 2) {

    //  }

    //this._update(true);
  },

  getValidMarkerId: function(markerUrl) {
    if (markerUrl.indexOf('url("#') >= 0) {
      // Fix for IE9, additional quotes are added to the <id
      var rawId = markerUrl.replace(/^url\(\"#/, '').replace(/\"\)$/, '')
      return this.id + rawId
    } else {
      markerUrl = markerUrl.replace(/^url\(#/, '')
      return this.id.concat(markerUrl.replace(/\)$/, ''))
    }
  },

  /**
   * Adds all necessary markers of this Edge to the SVG document.
   * Has to be called, while this.node is part of DOM.
   */
  addMarkers: function(defs) {
    this._markers.each(function(marker) {
      if (!defs.ownerDocument.getElementById(marker.value.id)) {
        marker.value.element = defs.appendChild(marker.value.element)
      }
    })
  },

  /**
   * Removes all necessary markers of this Edge from the SVG document.
   * Has to be called, while this.node is part of DOM.
   */
  removeMarkers: function() {
    var svgElement = this.node.ownerSVGElement
    if (svgElement) {
      var defs = svgElement.getElementsByTagNameNS(NAMESPACE_SVG, 'defs')
      if (defs.length > 0) {
        defs = defs[0]
        this._markers.each(function(marker) {
          var foundMarker = defs.ownerDocument.getElementById(marker.value.id)
          if (foundMarker) {
            marker.value.element = defs.removeChild(marker.value.element)
          }
        })
      }
    }
  },

  /**
   * Calls when a docker has changed
   */
  _dockerChanged: function() {
    //this._update(true);
    this._dockerUpdated = true
  },

  serialize: function() {
    var result = arguments.callee.$.serialize.apply(this)

    //add dockers triple
    var value = ''
    this._dockersByPath.each(
      function(pair) {
        pair.value.each(function(docker) {
          var position =
            docker.getDockedShape() && docker.referencePoint ? docker.referencePoint : docker.bounds.center()
          value = value.concat(position.x + ' ' + position.y + ' ')
        })

        value += ' # '
      }.bind(this)
    )
    result.push({
      name: 'dockers',
      prefix: 'oryx',
      value: value,
      type: 'literal'
    })

    //add parent triple dependant on the dockedShapes
    //TODO change this when canvas becomes a resource
    /*        var source = this.dockers.first().getDockedShape();
        var target = this.dockers.last().getDockedShape();
        var sharedParent;
        if (source && target) {
            //get shared parent
            while (source.parent) {
                source = source.parent;
                if (source instanceof ORYX.Core.Canvas) {
                    sharedParent = source;
                    break;
                }
                else {
                    var targetParent = target.parent;
                    var found;
                    while (targetParent) {
                        if (source === targetParent) {
                            sharedParent = source;
                            found = true;
                            break;
                        }
                        else {
                            targetParent = targetParent.parent;
                        }
                    }
                    if (found) {
                        break;
                    }
                }
            }
        }
        else 
            if (source) {
                sharedParent = source.parent;
            }
            else 
                if (target) {
                    sharedParent = target.parent;
                }
*/

    //if (sharedParent) {
    /*            result.push({
                name: 'parent',
                prefix: 'raziel',
                //value: '#' + ERDF.__stripHashes(sharedParent.resourceId),
                value: '#' + ERDF.__stripHashes(this.getCanvas().resourceId),
                type: 'resource'
            });*/
    //}

    //serialize target and source
    var lastDocker = this.dockers.last()

    var target = lastDocker.getDockedShape()

    if (target) {
      result.push({
        name: 'target',
        prefix: 'raziel',
        value: '#' + ERDF.__stripHashes(target.resourceId),
        type: 'resource'
      })
    }

    try {
      //result = this.getStencil().serialize(this, result);
      var serializeEvent = this.getStencil().serialize()

      /*
       * call serialize callback by reference, result should be found
       * in serializeEvent.result
       */
      if (serializeEvent.type) {
        serializeEvent.shape = this
        serializeEvent.data = result
        serializeEvent.result = undefined
        serializeEvent.forceExecution = true

        this._delegateEvent(serializeEvent)

        if (serializeEvent.result) {
          result = serializeEvent.result
        }
      }
    } catch (e) {}
    return result
  },

  deserialize: function(data) {
    try {
      //data = this.getStencil().deserialize(this, data);

      var deserializeEvent = this.getStencil().deserialize()

      /*
       * call serialize callback by reference, result should be found
       * in serializeEventInfo.result
       */
      if (deserializeEvent.type) {
        deserializeEvent.shape = this
        deserializeEvent.data = data
        deserializeEvent.result = undefined
        deserializeEvent.forceExecution = true

        this._delegateEvent(deserializeEvent)
        if (deserializeEvent.result) {
          data = deserializeEvent.result
        }
      }
    } catch (e) {}

    // Set the outgoing shapes
    var target = data.find(function(ser) {
      return ser.prefix + '-' + ser.name == 'raziel-target'
    })
    var targetShape
    if (target) {
      targetShape = this.getCanvas().getChildShapeByResourceId(target.value)
    }

    var outgoing = data.findAll(function(ser) {
      return ser.prefix + '-' + ser.name == 'raziel-outgoing'
    })
    outgoing.each(
      function(obj) {
        // TODO: Look at Canvas
        if (!this.parent) {
          return
        }

        // Set outgoing Shape
        var next = this.getCanvas().getChildShapeByResourceId(obj.value)

        if (next) {
          if (next == targetShape) {
            // If this is an edge, set the last docker to the next shape
            this.dockers.last().setDockedShape(next)
            this.dockers.last().setReferencePoint({ x: next.bounds.width() / 2.0, y: next.bounds.height() / 2.0 })
          } else if (next instanceof ORYX.Core.Edge) {
            //Set the first docker of the next shape
            next.dockers.first().setDockedShape(this)
            //next.dockers.first().setReferencePoint({x: this.bounds.width() / 2.0, y: this.bounds.height() / 2.0});
          } /*else if(next.dockers.length > 0) { //next is a node and next has a docker
					next.dockers.first().setDockedShape(this);
					next.dockers.first().setReferencePoint({x: this.bounds.width() / 2.0, y: this.bounds.height() / 2.0});
				}*/
        }
      }.bind(this)
    )

    var oryxDockers = data.find(function(obj) {
      return obj.prefix === 'oryx' && obj.name === 'dockers'
    })

    if (oryxDockers) {
      var dataByPath = oryxDockers.value
        .split('#')
        .without('')
        .without(' ')

      dataByPath.each(
        function(data, index) {
          var values = data
            .replace(/,/g, ' ')
            .split(' ')
            .without('')

          //for each docker two values must be defined
          if (values.length % 2 === 0) {
            var path = this._paths[index]

            if (path) {
              if (index === 0) {
                while (this._dockersByPath.get(path.id).length > 2) {
                  this.removeDocker(this._dockersByPath.get(path.id)[1])
                }
              } else {
                while (this._dockersByPath.get(path.id).length > 1) {
                  this.removeDocker(this._dockersByPath.get(path.id)[0])
                }
              }

              var dockersByPath = this._dockersByPath.get(path.id)

              if (index === 0) {
                //set position of first docker
                var x = parseFloat(values.shift())
                var y = parseFloat(values.shift())

                if (dockersByPath.first().getDockedShape()) {
                  dockersByPath.first().setReferencePoint({
                    x: x,
                    y: y
                  })
                } else {
                  dockersByPath.first().bounds.centerMoveTo(x, y)
                }
              }

              //set position of last docker
              y = parseFloat(values.pop())
              x = parseFloat(values.pop())

              if (dockersByPath.last().getDockedShape()) {
                dockersByPath.last().setReferencePoint({
                  x: x,
                  y: y
                })
              } else {
                dockersByPath.last().bounds.centerMoveTo(x, y)
              }

              //add additional dockers
              for (var i = 0; i < values.length; i++) {
                x = parseFloat(values[i])
                y = parseFloat(values[++i])

                var newDocker = this.createDocker()
                newDocker.bounds.centerMoveTo(x, y)

                //this.dockers = this.dockers.without(newDocker);
                //this.dockers.splice(this.dockers.indexOf(dockersByPath.last()), 0, newDocker);
                //dockersByPath.splice(this.dockers.indexOf(dockersByPath.last()), 0, newDocker);
              }
            }
          }
        }.bind(this)
      )
    } else {
      this.alignDockers()
    }

    arguments.callee.$.deserialize.apply(this, arguments)

    this._changed()
  },

  toString: function() {
    return this.getStencil().title() + ' ' + this.id
  },

  /**
   * @return {ORYX.Core.Shape} Returns last docked shape or null.
   */
  getTarget: function() {
    return this.dockers.last() ? this.dockers.last().getDockedShape() : null
  },

  /**
   * @return {ORYX.Core.Shape} Returns the first docked shape or null
   */
  getSource: function() {
    return this.dockers.first() ? this.dockers.first().getDockedShape() : null
  },

  /**
   * Checks whether the edge is at least docked to one shape.
   *
   * @return {boolean} True if edge is docked
   */
  isDocked: function() {
    var isDocked = false
    this.dockers.each(function(docker) {
      if (docker.isDocked()) {
        isDocked = true
        throw $break
      }
    })
    return isDocked
  },

  /**
   * Calls {@link ORYX.Core.AbstractShape#toJSON} and add a some stencil set information.
   */
  toJSON: function() {
    var json = arguments.callee.$.toJSON.apply(this, arguments)

    if (this.getTarget()) {
      json.target = {
        resourceId: this.getTarget().resourceId
      }
    }

    return json
  }
}
ORYX.Core.Edge = ORYX.Core.Shape.extend(ORYX.Core.Edge)
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

if (!ORYX) {
  var ORYX = {}
}
if (!ORYX.Plugins) {
  ORYX.Plugins = {}
}

/**
   This abstract plugin class can be used to build plugins on.
   It provides some more basic functionality like registering events (on*-handlers)...
   @example
    ORYX.Plugins.MyPlugin = ORYX.Plugins.AbstractPlugin.extend({
        construct: function() {
            // Call super class constructor
            arguments.callee.$.construct.apply(this, arguments);
            
            [...]
        },
        [...]
    });
   
   @class ORYX.Plugins.AbstractPlugin
   @constructor Creates a new instance
   @author Willi Tscheschner
*/
ORYX.Plugins.AbstractPlugin = Clazz.extend({
  /**
   * The facade which offer editor-specific functionality
   * @type Facade
   * @memberOf ORYX.Plugins.AbstractPlugin.prototype
   */
  facade: null,

  construct: function(facade) {
    this.facade = facade

    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_LOADED, this.onLoaded.bind(this))
  },

  /**
       Overwrite to handle load event. TODO: Document params!!!
       @methodOf ORYX.Plugins.AbstractPlugin.prototype
    */
  onLoaded: function() {},

  /**
       Overwrite to handle selection changed event. TODO: Document params!!!
       @methodOf ORYX.Plugins.AbstractPlugin.prototype
    */
  onSelectionChanged: function() {},

  /**
       Show overlay on given shape.
       @methodOf ORYX.Plugins.AbstractPlugin.prototype
       @example
       showOverlay(
           myShape,
           { stroke: "green" },
           ORYX.Editor.graft("http://www.w3.org/2000/svg", null, ['path', {
               "title": "Click the element to execute it!",
               "stroke-width": 2.0,
               "stroke": "black",
               "d": "M0,-5 L5,0 L0,5 Z",
               "line-captions": "round"
           }])
       )
       @param {Oryx.XXX.Shape[]} shapes One shape or array of shapes the overlay should be put on
       @param {Oryx.XXX.Attributes} attributes some attributes...
       @param {Oryx.svg.node} svgNode The svg node which should be used as overlay
       @param {String} [svgNode="NW"] The svg node position where the overlay should be placed
    */
  showOverlay: function(shapes, attributes, svgNode, svgNodePosition) {
    if (!(shapes instanceof Array)) {
      shapes = [shapes]
    }

    // Define Shapes
    shapes = shapes
      .map(
        function(shape) {
          var el = shape
          if (typeof shape == 'string') {
            el = this.facade.getCanvas().getChildShapeByResourceId(shape)
            el = el || this.facade.getCanvas().getChildById(shape, true)
          }
          return el
        }.bind(this)
      )
      .compact()

    // Define unified id
    if (!this.overlayID) {
      this.overlayID = this.type + ORYX.Editor.provideId()
    }

    this.facade.raiseEvent({
      type: ORYX.CONFIG.EVENT_OVERLAY_SHOW,
      id: this.overlayID,
      shapes: shapes,
      attributes: attributes,
      node: svgNode,
      nodePosition: svgNodePosition || 'NW'
    })
  },

  /**
       Hide current overlay.
       @methodOf ORYX.Plugins.AbstractPlugin.prototype
    */
  hideOverlay: function() {
    this.facade.raiseEvent({
      type: ORYX.CONFIG.EVENT_OVERLAY_HIDE,
      id: this.overlayID
    })
  },

  /**
       Does a transformation with the given xslt stylesheet.
       @methodOf ORYX.Plugins.AbstractPlugin.prototype
       @param {String} data The data (e.g. eRDF) which should be transformed
       @param {String} stylesheet URL of a stylesheet which should be used for transforming data.
    */
  doTransform: function(data, stylesheet) {
    if (!stylesheet || !data) {
      return ''
    }

    var parser = new DOMParser()
    var parsedData = parser.parseFromString(data, 'text/xml')
    source = stylesheet
    new Ajax.Request(source, {
      asynchronous: false,
      method: 'get',
      onSuccess: function(transport) {
        xsl = transport.responseText
      }.bind(this),
      onFailure: function(transport) {
        ORYX.Log.error('XSL load failed' + transport)
      }.bind(this)
    })
    var xsltProcessor = new XSLTProcessor()
    var domParser = new DOMParser()
    var xslObject = domParser.parseFromString(xsl, 'text/xml')
    xsltProcessor.importStylesheet(xslObject)

    try {
      var newData = xsltProcessor.transformToFragment(parsedData, document)
      var serializedData = new XMLSerializer().serializeToString(newData)

      /* Firefox 2 to 3 problem?! */
      serializedData = !serializedData.startsWith('<?xml')
        ? '<?xml version="1.0" encoding="UTF-8"?>' + serializedData
        : serializedData

      return serializedData
    } catch (error) {
      return -1
    }
  },

  /**
   * Opens a new window that shows the given XML content.
   * @methodOf ORYX.Plugins.AbstractPlugin.prototype
   * @param {Object} content The XML content to be shown.
   * @example
   * openDownloadWindow( "my.xml", "<exampleXML />" );
   */
  openXMLWindow: function(content) {
    var win = window.open(
      'data:application/xml,' + encodeURIComponent(content),
      '_blank',
      'resizable=yes,width=600,height=600,toolbar=0,scrollbars=yes'
    )
  },

  /**
   * Opens a download window for downloading the given content.
   * @methodOf ORYX.Plugins.AbstractPlugin.prototype
   * @param {String} filename The content's file name
   * @param {String} content The content to download
   */
  openDownloadWindow: function(filename, content) {
    var win = window.open('')
    if (win != null) {
      win.document.open()
      win.document.write('<html><body>')
      var submitForm = win.document.createElement('form')
      win.document.body.appendChild(submitForm)

      var createHiddenElement = function(name, value) {
        var newElement = document.createElement('input')
        newElement.name = name
        newElement.type = 'hidden'
        newElement.value = value
        return newElement
      }

      submitForm.appendChild(createHiddenElement('download', content))
      submitForm.appendChild(createHiddenElement('file', filename))

      submitForm.method = 'POST'
      win.document.write('</body></html>')
      win.document.close()
      submitForm.action = ORYX.PATH + '/download'
      submitForm.submit()
    }
  },

  /**
   * Serializes DOM.
   * @methodOf ORYX.Plugins.AbstractPlugin.prototype
   * @type {String} Serialized DOM
   */
  getSerializedDOM: function() {
    // Force to set all resource IDs
    var serializedDOM = DataManager.serializeDOM(this.facade)

    //add namespaces
    serializedDOM =
      '<?xml version="1.0" encoding="utf-8"?>' +
      '<html xmlns="http://www.w3.org/1999/xhtml" ' +
      'xmlns:b3mn="http://b3mn.org/2007/b3mn" ' +
      'xmlns:ext="http://b3mn.org/2007/ext" ' +
      'xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" ' +
      'xmlns:atom="http://b3mn.org/2007/atom+xhtml">' +
      '<head profile="http://purl.org/NET/erdf/profile">' +
      '<link rel="schema.dc" href="http://purl.org/dc/elements/1.1/" />' +
      '<link rel="schema.dcTerms" href="http://purl.org/dc/terms/ " />' +
      '<link rel="schema.b3mn" href="http://b3mn.org" />' +
      '<link rel="schema.oryx" href="http://oryx-editor.org/" />' +
      '<link rel="schema.raziel" href="http://raziel.org/" />' +
      '<base href="' +
      location.href.split('?')[0] +
      '" />' +
      '</head><body>' +
      serializedDOM +
      '</body></html>'

    return serializedDOM
  },

  /**
   * Sets the editor in read only mode: Edges/ dockers cannot be moved anymore,
   * shapes cannot be selected anymore.
   * @methodOf ORYX.Plugins.AbstractPlugin.prototype
   */
  enableReadOnlyMode: function() {
    //Edges cannot be moved anymore
    this.facade.disableEvent(ORYX.CONFIG.EVENT_MOUSEDOWN)

    // Stop the user from editing the diagram while the plugin is active
    this._stopSelectionChange = function() {
      if (this.facade.getSelection().length > 0) {
        this.facade.setSelection([])
      }
    }
    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_SELECTION_CHANGED, this._stopSelectionChange.bind(this))
  },
  /**
   * Disables read only mode, see @see
   * @methodOf ORYX.Plugins.AbstractPlugin.prototype
   * @see ORYX.Plugins.AbstractPlugin.prototype.enableReadOnlyMode
   */
  disableReadOnlyMode: function() {
    // Edges can be moved now again
    this.facade.enableEvent(ORYX.CONFIG.EVENT_MOUSEDOWN)

    if (this._stopSelectionChange) {
      this.facade.unregisterOnEvent(ORYX.CONFIG.EVENT_SELECTION_CHANGED, this._stopSelectionChange.bind(this))
      this._stopSelectionChange = undefined
    }
  },

  /**
   * Extracts RDF from DOM.
   * @methodOf ORYX.Plugins.AbstractPlugin.prototype
   * @type {String} Extracted RFD. Null if there are transformation errors.
   */
  getRDFFromDOM: function() {
    //convert to RDF
    try {
      var xsl = ''
      source = ORYX.PATH + 'lib/extract-rdf.xsl'
      new Ajax.Request(source, {
        asynchronous: false,
        method: 'get',
        onSuccess: function(transport) {
          xsl = transport.responseText
        }.bind(this),
        onFailure: function(transport) {
          ORYX.Log.error('XSL load failed' + transport)
        }.bind(this)
      })

      var domParser = new DOMParser()
      var xmlObject = domParser.parseFromString(this.getSerializedDOM(), 'text/xml')
      var xslObject = domParser.parseFromString(xsl, 'text/xml')
      var xsltProcessor = new XSLTProcessor()
      xsltProcessor.importStylesheet(xslObject)
      var result = xsltProcessor.transformToFragment(xmlObject, document)

      var serializer = new XMLSerializer()

      return serializer.serializeToString(result)
    } catch (e) {
      console.log('error serializing ' + e)
      return ''
    }
  },

  /**
   * Checks if a certain stencil set is loaded right now.
   *
   */
  isStencilSetExtensionLoaded: function(stencilSetExtensionNamespace) {
    return this.facade
      .getStencilSets()
      .values()
      .any(
        function(ss) {
          return ss
            .extensions()
            .keys()
            .any(
              function(extensionKey) {
                return extensionKey == stencilSetExtensionNamespace
              }.bind(this)
            )
        }.bind(this)
      )
  },

  /**
   * Raises an event so that registered layouters does
   * have the posiblility to layout the given shapes
   * For further reading, have a look into the AbstractLayouter
   * class
   * @param {Object} shapes
   */
  doLayout: function(shapes) {
    // Raises a do layout event
    if (this.facade.raiseEvent) {
      this.facade.raiseEvent({
        type: ORYX.CONFIG.EVENT_LAYOUT,
        shapes: shapes
      })
    } else {
      this.facade.handleEvents({
        type: ORYX.CONFIG.EVENT_LAYOUT,
        shapes: shapes
      })
    }
  },

  /**
   * Does a primitive layouting with the incoming/outgoing
   * edges (set the dockers to the right position) and if
   * necessary, it will be called the real layouting
   * @param {ORYX.Core.Node} node
   * @param {Array} edges
   */
  layoutEdges: function(node, allEdges, offset) {
    if (!this.facade.isExecutingCommands()) {
      return
    }

    var Command = ORYX.Core.Command.extend({
      construct: function(edges, node, offset, plugin) {
        this.edges = edges
        this.node = node
        this.plugin = plugin
        this.offset = offset

        // Get the new absolute center
        var center = node.absoluteXY()
        this.ulo = { x: center.x - offset.x, y: center.y - offset.y }
      },
      execute: function() {
        if (this.changes) {
          this.executeAgain()
          return
        } else {
          this.changes = []
          this.edges.each(
            function(edge) {
              this.changes.push({
                edge: edge,
                oldDockerPositions: edge.dockers.map(function(r) {
                  return r.bounds.center()
                })
              })
            }.bind(this)
          )
        }

        // Find all edges, which are related to the node and
        // have more than two dockers
        this.edges
          // Find all edges with more than two dockers
          .findAll(
            function(r) {
              return r.dockers.length > 2
            }.bind(this)
          )
          // For every edge, check second and one before last docker
          // if there are horizontal/vertical on the same level
          // and if so, align the the bounds
          .each(
            function(edge) {
              if (edge.dockers.first().getDockedShape() === this.node) {
                var second = edge.dockers[1]
                if (this.align(second.bounds, edge.dockers.first())) {
                  second.update()
                }
              } else if (edge.dockers.last().getDockedShape() === this.node) {
                var beforeLast = edge.dockers[edge.dockers.length - 2]
                if (this.align(beforeLast.bounds, edge.dockers.last())) {
                  beforeLast.update()
                }
              }
              edge._update(true)
              edge.removeUnusedDockers()
              if (this.isBendPointIncluded(edge)) {
                this.plugin.doLayout(edge)
                return
              }
            }.bind(this)
          )

        // Find all edges, which have only to dockers
        // and is located horizontal/vertical.
        // Do layout with those edges
        this.edges
          // Find all edges with exactly two dockers
          .each(
            function(edge) {
              if (edge.dockers.length == 2) {
                var p1 = edge.dockers.first().getAbsoluteReferencePoint() || edge.dockers.first().bounds.center()
                var p2 = edge.dockers.last().getAbsoluteReferencePoint() || edge.dockers.first().bounds.center()
                // Find all horizontal/vertical edges
                if (
                  Math.abs(-Math.abs(p1.x - p2.x) + Math.abs(this.offset.x)) < 2 ||
                  Math.abs(-Math.abs(p1.y - p2.y) + Math.abs(this.offset.y)) < 2
                ) {
                  this.plugin.doLayout(edge)
                }
              }
            }.bind(this)
          )

        this.edges.each(
          function(edge, i) {
            this.changes[i].dockerPositions = edge.dockers.map(function(r) {
              return r.bounds.center()
            })
          }.bind(this)
        )
      },
      /**
       * Align the bounds if the center is
       * the same than the old center
       * @params {Object} bounds
       * @params {Object} bounds2
       */
      align: function(bounds, refDocker) {
        var abRef = refDocker.getAbsoluteReferencePoint() || refDocker.bounds.center()

        var xdif = bounds.center().x - abRef.x
        var ydif = bounds.center().y - abRef.y
        if (Math.abs(-Math.abs(xdif) + Math.abs(this.offset.x)) < 3 && this.offset.xs === undefined) {
          bounds.moveBy({ x: -xdif, y: 0 })
        }
        if (Math.abs(-Math.abs(ydif) + Math.abs(this.offset.y)) < 3 && this.offset.ys === undefined) {
          bounds.moveBy({ y: -ydif, x: 0 })
        }

        if (this.offset.xs !== undefined || this.offset.ys !== undefined) {
          var absPXY = refDocker.getDockedShape().absoluteXY()
          xdif = bounds.center().x - (absPXY.x + (abRef.x - absPXY.x) / this.offset.xs)
          ydif = bounds.center().y - (absPXY.y + (abRef.y - absPXY.y) / this.offset.ys)

          if (Math.abs(-Math.abs(xdif) + Math.abs(this.offset.x)) < 3) {
            bounds.moveBy({ x: -(bounds.center().x - abRef.x), y: 0 })
          }

          if (Math.abs(-Math.abs(ydif) + Math.abs(this.offset.y)) < 3) {
            bounds.moveBy({ y: -(bounds.center().y - abRef.y), x: 0 })
          }
        }
      },

      /**
       * Returns a TRUE if there are bend point which overlay the shape
       */
      isBendPointIncluded: function(edge) {
        // Get absolute bounds
        var ab = edge.dockers.first().getDockedShape()
        var bb = edge.dockers.last().getDockedShape()

        if (ab) {
          ab = ab.absoluteBounds()
          ab.widen(5)
        }

        if (bb) {
          bb = bb.absoluteBounds()
          bb.widen(20) // Wide with 20 because of the arrow from the edge
        }

        return edge.dockers.any(function(docker, i) {
          var c = docker.bounds.center()
          // Dont count first and last
          return (
            i != 0 &&
            i != edge.dockers.length - 1 &&
            // Check if the point is included to the absolute bounds
            ((ab && ab.isIncluded(c)) || (bb && bb.isIncluded(c)))
          )
        })
      },

      removeAllDocker: function(edge) {
        edge.dockers.slice(1, edge.dockers.length - 1).each(function(docker) {
          edge.removeDocker(docker)
        })
      },
      executeAgain: function() {
        this.changes.each(
          function(change) {
            // Reset the dockers
            this.removeAllDocker(change.edge)
            change.dockerPositions.each(
              function(pos, i) {
                if (i == 0 || i == change.dockerPositions.length - 1) {
                  return
                }
                var docker = change.edge.createDocker(undefined, pos)
                docker.bounds.centerMoveTo(pos)
                docker.update()
              }.bind(this)
            )
            change.edge._update(true)
          }.bind(this)
        )
      },
      rollback: function() {
        this.changes.each(
          function(change) {
            // Reset the dockers
            this.removeAllDocker(change.edge)
            change.oldDockerPositions.each(
              function(pos, i) {
                if (i == 0 || i == change.oldDockerPositions.length - 1) {
                  return
                }
                var docker = change.edge.createDocker(undefined, pos)
                docker.bounds.centerMoveTo(pos)
                docker.update()
              }.bind(this)
            )
            change.edge._update(true)
          }.bind(this)
        )
      }
    })

    this.facade.executeCommands([new Command(allEdges, node, offset, this)])
  }
})
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

if (!ORYX) {
  var ORYX = {}
}
if (!ORYX.Plugins) {
  ORYX.Plugins = {}
}

/**
   This abstract plugin implements the core behaviour of layout
   
   @class ORYX.Plugins.AbstractLayouter
   @constructor Creates a new instance
   @author Willi Tscheschner
*/
ORYX.Plugins.AbstractLayouter = ORYX.Plugins.AbstractPlugin.extend({
  /**
   * 'layouted' defined all types of shapes which will be layouted.
   * It can be one value or an array of values. The value
   * can be a Stencil ID (as String) or an class type of either
   * a ORYX.Core.Node or ORYX.Core.Edge
   * @type Array|String|Object
   * @memberOf ORYX.Plugins.AbstractLayouter.prototype
   */
  layouted: [],

  /**
   * Constructor
   * @param {Object} facade
   * @memberOf ORYX.Plugins.AbstractLayouter.prototype
   */
  construct: function(facade) {
    arguments.callee.$.construct.apply(this, arguments)

    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_LAYOUT, this._initLayout.bind(this))
  },

  /**
   * Proofs if this shape should be layouted or not
   * @param {Object} shape
   * @memberOf ORYX.Plugins.AbstractLayouter.prototype
   */
  isIncludedInLayout: function(shape) {
    if (!(this.layouted instanceof Array)) {
      this.layouted = [this.layouted].compact()
    }

    // If there are no elements
    if (this.layouted.length <= 0) {
      // Return TRUE
      return true
    }

    // Return TRUE if there is any correlation between
    // the 'layouted' attribute and the shape themselve.
    return this.layouted.any(function(s) {
      if (typeof s == 'string') {
        return shape
          .getStencil()
          .id()
          .include(s)
      } else {
        return shape instanceof s
      }
    })
  },

  /**
   * Callback to start the layouting
   * @param {Object} event Layout event
   * @param {Object} shapes Given shapes
   * @memberOf ORYX.Plugins.AbstractLayouter.prototype
   */
  _initLayout: function(event) {
    // Get the shapes
    var shapes = [event.shapes].flatten().compact()

    // Find all shapes which should be layouted
    var toLayout = shapes.findAll(
      function(shape) {
        return this.isIncludedInLayout(shape)
      }.bind(this)
    )

    // If there are shapes left
    if (toLayout.length > 0) {
      // Do layout
      this.layout(toLayout)
    }
  },

  /**
   * Implementation of layouting a set on shapes
   * @param {Object} shapes Given shapes
   * @memberOf ORYX.Plugins.AbstractLayouter.prototype
   */
  layout: function(shapes) {
    throw new Error('Layouter has to implement the layout function.')
  }
})
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

if (!ORYX.Plugins) ORYX.Plugins = new Object()

ORYX.Plugins.Edit = Clazz.extend({
  construct: function(facade) {
    this.facade = facade
    this.clipboard = new ORYX.Plugins.Edit.ClipBoard()

    //this.facade.registerOnEvent(ORYX.CONFIG.EVENT_KEYDOWN, this.keyHandler.bind(this));

    this.facade.offer({
      name: ORYX.I18N.Edit.cut,
      description: ORYX.I18N.Edit.cutDesc,
      icon: ORYX.PATH + 'images/cut.png',
      keyCodes: [
        {
          metaKeys: [ORYX.CONFIG.META_KEY_META_CTRL],
          keyCode: 88,
          keyAction: ORYX.CONFIG.KEY_ACTION_DOWN
        }
      ],
      functionality: this.callEdit.bind(this, this.editCut),
      group: ORYX.I18N.Edit.group,
      index: 1,
      minShape: 1
    })

    this.facade.offer({
      name: ORYX.I18N.Edit.copy,
      description: ORYX.I18N.Edit.copyDesc,
      icon: ORYX.PATH + 'images/page_copy.png',
      keyCodes: [
        {
          metaKeys: [ORYX.CONFIG.META_KEY_META_CTRL],
          keyCode: 67,
          keyAction: ORYX.CONFIG.KEY_ACTION_DOWN
        }
      ],
      functionality: this.callEdit.bind(this, this.editCopy, [true, false]),
      group: ORYX.I18N.Edit.group,
      index: 2,
      minShape: 1
    })

    this.facade.offer({
      name: ORYX.I18N.Edit.paste,
      description: ORYX.I18N.Edit.pasteDesc,
      icon: ORYX.PATH + 'images/page_paste.png',
      keyCodes: [
        {
          metaKeys: [ORYX.CONFIG.META_KEY_META_CTRL],
          keyCode: 86,
          keyAction: ORYX.CONFIG.KEY_ACTION_DOWN
        }
      ],
      functionality: this.callEdit.bind(this, this.editPaste),
      isEnabled: this.clipboard.isOccupied.bind(this.clipboard),
      group: ORYX.I18N.Edit.group,
      index: 3,
      minShape: 0,
      maxShape: 0
    })

    this.facade.offer({
      name: ORYX.I18N.Edit.del,
      description: ORYX.I18N.Edit.delDesc,
      icon: ORYX.PATH + 'images/cross.png',
      keyCodes: [
        {
          metaKeys: [ORYX.CONFIG.META_KEY_META_CTRL],
          keyCode: 8,
          keyAction: ORYX.CONFIG.KEY_ACTION_DOWN
        },
        {
          keyCode: 46,
          keyAction: ORYX.CONFIG.KEY_ACTION_DOWN
        }
      ],
      functionality: this.callEdit.bind(this, this.editDelete),
      group: ORYX.I18N.Edit.group,
      index: 4,
      minShape: 1
    })
  },

  callEdit: function(fn, args) {
    window.setTimeout(
      function() {
        fn.apply(this, args instanceof Array ? args : [])
      }.bind(this),
      1
    )
  },

  /**
   * Handles the mouse down event and starts the copy-move-paste action, if
   * control or meta key is pressed.
   */
  handleMouseDown: function(event) {
    if (this._controlPressed) {
      this._controlPressed = false
      this.editCopy()
      //			console.log("copiedEle: %0",this.clipboard.shapesAsJson)
      //			console.log("mousevent: %o",event)
      this.editPaste()
      event.forceExecution = true
      this.facade.raiseEvent(event, this.clipboard.shapesAsJson)
    }
  },

  /**
   * The key handler for this plugin. Every action from the set of cut, copy,
   * paste and delete should be accessible trough simple keyboard shortcuts.
   * This method checks whether any event triggers one of those actions.
   *
   * @param {Object} event The keyboard event that should be analysed for
   *     triggering of this plugin.
   */
  //    keyHandler: function(event){
  //        //TODO document what event.which is.
  //
  //        ORYX.Log.debug("edit.js handles a keyEvent.");
  //
  //        // assure we have the current event.
  //        if (!event)
  //            event = window.event;
  //
  //
  //        // get the currently pressed key and state of control key.
  //        var pressedKey = event.which || event.keyCode;
  //        var ctrlPressed = event.ctrlKey;
  //
  //        // if the object is to be deleted, do so, and return immediately.
  //        if ((pressedKey == ORYX.CONFIG.KEY_CODE_DELETE) ||
  //        ((pressedKey == ORYX.CONFIG.KEY_CODE_BACKSPACE) &&
  //        (event.metaKey || event.appleMetaKey))) {
  //
  //            ORYX.Log.debug("edit.js deletes the shape.");
  //            this.editDelete();
  //            return;
  //        }
  //
  //         // if control key is not pressed, we're not interested anymore.
  //         if (!ctrlPressed)
  //         return;
  //
  //         // when ctrl is pressed, switch trough the possibilities.
  //         switch (pressedKey) {
  //
  //	         // cut.
  //	         case ORYX.CONFIG.KEY_CODE_X:
  //	         this.editCut();
  //	         break;
  //
  //	         // copy.
  //	         case ORYX.CONFIG.KEY_CODE_C:
  //	         this.editCopy();
  //	         break;
  //
  //	         // paste.
  //	         case ORYX.CONFIG.KEY_CODE_V:
  //	         this.editPaste();
  //	         break;
  //         }
  //    },

  /**
   * Returns a list of shapes which should be considered while copying.
   * Besides the shapes of given ones, edges and attached nodes are added to the result set.
   * If one of the given shape is a child of another given shape, it is not put into the result.
   */
  getAllShapesToConsider: function(shapes) {
    var shapesToConsider = [] // only top-level shapes
    var childShapesToConsider = [] // all child shapes of top-level shapes

    shapes.each(
      function(shape) {
        //Throw away these shapes which have a parent in given shapes
        isChildShapeOfAnother = shapes.any(function(s2) {
          return s2.hasChildShape(shape)
        })
        if (isChildShapeOfAnother) return

        // This shape should be considered
        shapesToConsider.push(shape)
        // Consider attached nodes (e.g. intermediate events)
        if (shape instanceof ORYX.Core.Node) {
          var attached = shape.getOutgoingNodes()
          attached = attached.findAll(function(a) {
            return !shapes.include(a)
          })
          shapesToConsider = shapesToConsider.concat(attached)
        }

        childShapesToConsider = childShapesToConsider.concat(shape.getChildShapes(true))
      }.bind(this)
    )

    // All edges between considered child shapes should be considered
    // Look for these edges having incoming and outgoing in childShapesToConsider
    var edgesToConsider = this.facade
      .getCanvas()
      .getChildEdges()
      .select(function(edge) {
        // Ignore if already added
        if (shapesToConsider.include(edge)) return false
        // Ignore if there are no docked shapes
        if (edge.getAllDockedShapes().size() === 0) return false
        // True if all docked shapes are in considered child shapes
        return edge.getAllDockedShapes().all(function(shape) {
          // Remember: Edges can have other edges on outgoing, that is why edges must not be included in childShapesToConsider
          return shape instanceof ORYX.Core.Edge || childShapesToConsider.include(shape)
        })
      })
    shapesToConsider = shapesToConsider.concat(edgesToConsider)

    return shapesToConsider
  },

  /**
   * Performs the cut operation by first copy-ing and then deleting the
   * current selection.
   */
  editCut: function() {
    //TODO document why this returns false.
    //TODO document what the magic boolean parameters are supposed to do.

    this.editCopy(false, true)
    this.editDelete(true)
    return false
  },

  /**
   * Performs the copy operation.
   * @param {Object} will_not_update ??
   */
  editCopy: function(will_update, useNoOffset) {
    var selection = this.facade.getSelection()

    //if the selection is empty, do not remove the previously copied elements
    if (selection.length == 0) return

    this.clipboard.refresh(
      selection,
      this.getAllShapesToConsider(selection),
      this.facade
        .getCanvas()
        .getStencil()
        .stencilSet()
        .namespace(),
      useNoOffset
    )

    if (will_update) this.facade.updateSelection()
  },

  /**
   * Performs the paste operation.
   */
  editPaste: function() {
    // Create a new canvas with childShapes
    //and stencilset namespace to be JSON Import conform
    var canvas = {
      childShapes: this.clipboard.shapesAsJson,
      stencilset: {
        namespace: this.clipboard.SSnamespace
      }
    }
    // Apply json helper to iterate over json object
    jQuery.extend(canvas, ORYX.Core.AbstractShape.JSONHelper)

    var childShapeResourceIds = canvas.getChildShapes(true).pluck('resourceId')
    var outgoings = {}
    // Iterate over all shapes
    canvas.eachChild(
      function(shape, parent) {
        // Throw away these references where referenced shape isn't copied
        shape.outgoing = shape.outgoing.select(function(out) {
          return childShapeResourceIds.include(out.resourceId)
        })
        shape.outgoing.each(function(out) {
          if (!outgoings[out.resourceId]) {
            outgoings[out.resourceId] = []
          }
          outgoings[out.resourceId].push(shape)
        })

        return shape
      }.bind(this),
      true,
      true
    )

    // Iterate over all shapes
    canvas.eachChild(
      function(shape, parent) {
        // Check if there has a valid target
        if (shape.target && !childShapeResourceIds.include(shape.target.resourceId)) {
          shape.target = undefined
          shape.targetRemoved = true
        }

        // Check if the first docker is removed
        if (
          shape.dockers &&
          shape.dockers.length >= 1 &&
          shape.dockers[0].getDocker &&
          ((shape.dockers[0].getDocker().getDockedShape() &&
            !childShapeResourceIds.include(shape.dockers[0].getDocker().getDockedShape().resourceId)) ||
            (!shape.getShape().dockers[0].getDockedShape() && !outgoings[shape.resourceId]))
        ) {
          shape.sourceRemoved = true
        }

        return shape
      }.bind(this),
      true,
      true
    )

    // Iterate over top-level shapes
    canvas.eachChild(
      function(shape, parent) {
        // All top-level shapes should get an offset in their bounds
        // Move the shape occording to COPY_MOVE_OFFSET
        if (this.clipboard.useOffset) {
          shape.bounds = {
            lowerRight: {
              x: shape.bounds.lowerRight.x + ORYX.CONFIG.COPY_MOVE_OFFSET,
              y: shape.bounds.lowerRight.y + ORYX.CONFIG.COPY_MOVE_OFFSET
            },
            upperLeft: {
              x: shape.bounds.upperLeft.x + ORYX.CONFIG.COPY_MOVE_OFFSET,
              y: shape.bounds.upperLeft.y + ORYX.CONFIG.COPY_MOVE_OFFSET
            }
          }
        }
        // Only apply offset to shapes with a target
        if (shape.dockers) {
          shape.dockers = shape.dockers.map(
            function(docker, i) {
              // If shape had a target but the copied does not have anyone anymore,
              // migrate the relative dockers to absolute ones.
              if (
                (shape.targetRemoved === true && i == shape.dockers.length - 1 && docker.getDocker) ||
                (shape.sourceRemoved === true && i == 0 && docker.getDocker)
              ) {
                docker = docker.getDocker().bounds.center()
              }

              // If it is the first docker and it has a docked shape,
              // just return the coordinates
              if (
                (i == 0 &&
                  docker.getDocker instanceof Function &&
                  shape.sourceRemoved !== true &&
                  (docker.getDocker().getDockedShape() ||
                    ((outgoings[shape.resourceId] || []).length > 0 &&
                      (!(shape.getShape() instanceof ORYX.Core.Node) ||
                        outgoings[shape.resourceId][0].getShape() instanceof ORYX.Core.Node)))) ||
                (i == shape.dockers.length - 1 &&
                  docker.getDocker instanceof Function &&
                  shape.targetRemoved !== true &&
                  (docker.getDocker().getDockedShape() || shape.target))
              ) {
                return {
                  x: docker.x,
                  y: docker.y,
                  getDocker: docker.getDocker
                }
              } else if (this.clipboard.useOffset) {
                return {
                  x: docker.x + ORYX.CONFIG.COPY_MOVE_OFFSET,
                  y: docker.y + ORYX.CONFIG.COPY_MOVE_OFFSET,
                  getDocker: docker.getDocker
                }
              } else {
                return {
                  x: docker.x,
                  y: docker.y,
                  getDocker: docker.getDocker
                }
              }
            }.bind(this)
          )
        } else if (
          shape.getShape() instanceof ORYX.Core.Node &&
          shape.dockers &&
          shape.dockers.length > 0 &&
          (!shape.dockers.first().getDocker ||
            shape.sourceRemoved === true ||
            !(
              shape.dockers
                .first()
                .getDocker()
                .getDockedShape() || outgoings[shape.resourceId]
            ))
        ) {
          shape.dockers = shape.dockers.map(
            function(docker, i) {
              if (shape.sourceRemoved === true && i == 0 && docker.getDocker) {
                docker = docker.getDocker().bounds.center()
              }

              if (this.clipboard.useOffset) {
                return {
                  x: docker.x + ORYX.CONFIG.COPY_MOVE_OFFSET,
                  y: docker.y + ORYX.CONFIG.COPY_MOVE_OFFSET,
                  getDocker: docker.getDocker
                }
              } else {
                return {
                  x: docker.x,
                  y: docker.y,
                  getDocker: docker.getDocker
                }
              }
            }.bind(this)
          )
        }

        return shape
      }.bind(this),
      false,
      true
    )

    this.clipboard.useOffset = true
    this.facade.importJSON(canvas)
  },

  /**
   * Performs the delete operation. No more asking.
   */
  editDelete: function() {
    var selection = this.facade.getSelection()

    if (selection.length > 0) {
      //only update the command stack if something was performed...
      var clipboard = new ORYX.Plugins.Edit.ClipBoard()
      clipboard.refresh(selection, this.getAllShapesToConsider(selection))

      var command = new ORYX.Plugins.Edit.DeleteCommand(clipboard, this.facade)

      this.facade.executeCommands([command])
    }
  }
})

ORYX.Plugins.Edit.ClipBoard = Clazz.extend({
  construct: function() {
    this.shapesAsJson = []
    this.selection = []
    this.SSnamespace = ''
    this.useOffset = true
  },
  isOccupied: function() {
    return this.shapesAsJson.length > 0
  },
  refresh: function(selection, shapes, namespace, useNoOffset) {
    this.selection = selection
    this.SSnamespace = namespace
    // Store outgoings, targets and parents to restore them later on
    this.outgoings = {}
    this.parents = {}
    this.targets = {}
    this.useOffset = useNoOffset !== true

    this.shapesAsJson = shapes.map(function(shape) {
      var s = shape.toJSON()
      s.parent = { resourceId: shape.getParentShape().resourceId }
      s.parentIndex = shape
        .getParentShape()
        .getChildShapes()
        .indexOf(shape)
      return s
    })
  }
})

ORYX.Plugins.Edit.DeleteCommand = ORYX.Core.Command.extend({
  construct: function(clipboard, facade) {
    this.clipboard = clipboard
    this.shapesAsJson = clipboard.shapesAsJson
    this.facade = facade

    // Store dockers of deleted shapes to restore connections
    this.dockers = this.shapesAsJson
      .map(function(shapeAsJson) {
        var shape = shapeAsJson.getShape()
        var incomingDockers = shape.getIncomingShapes().map(function(s) {
          return s.getDockers().last()
        })
        var outgoingDockers = shape.getOutgoingShapes().map(function(s) {
          return s.getDockers().first()
        })
        var dockers = shape
          .getDockers()
          .concat(incomingDockers, outgoingDockers)
          .compact()
          .map(function(docker) {
            return {
              object: docker,
              referencePoint: docker.referencePoint,
              dockedShape: docker.getDockedShape()
            }
          })
        return dockers
      })
      .flatten()
  },
  execute: function() {
    this.shapesAsJson.each(
      function(shapeAsJson) {
        // Delete shape
        this.facade.deleteShape(shapeAsJson.getShape())
      }.bind(this)
    )

    this.facade.setSelection([])
    this.facade.getCanvas().update()
    this.facade.updateSelection()
    this.facade.handleEvents({ type: ORYX.CONFIG.ACTION_DELETE_COMPLETED })
  },
  rollback: function() {
    this.shapesAsJson.each(
      function(shapeAsJson) {
        var shape = shapeAsJson.getShape()
        var parent =
          this.facade.getCanvas().getChildShapeByResourceId(shapeAsJson.parent.resourceId) || this.facade.getCanvas()
        parent.add(shape, shape.parentIndex)
      }.bind(this)
    )

    //reconnect shapes
    this.dockers.each(
      function(d) {
        d.object.setDockedShape(d.dockedShape)
        d.object.setReferencePoint(d.referencePoint)
      }.bind(this)
    )

    this.facade.setSelection(this.selectedShapes)
    this.facade.getCanvas().update()
    this.facade.updateSelection()
  }
})
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

/**
 * @namespace Oryx name space for plugins
 * @name ORYX.Plugins
 */
if (!ORYX.Plugins) ORYX.Plugins = new Object()

/**
 * The view plugin offers all of zooming functionality accessible over the
 * tool bar. This are zoom in, zoom out, zoom to standard, zoom fit to model.
 *
 * @class ORYX.Plugins.View
 * @extends Clazz
 * @param {Object} facade The editor facade for plugins.
 */
ORYX.Plugins.View = {
  /** @lends ORYX.Plugins.View.prototype */
  facade: undefined,

  construct: function(facade, ownPluginData) {
    this.facade = facade
    //Standard Values
    this.zoomLevel = 1.0
    this.maxFitToScreenLevel = 1.5
    this.minZoomLevel = 0.1
    this.maxZoomLevel = 2.5
    this.diff = 5 //difference between canvas and view port, s.th. like toolbar??

    //Read properties
    if (ownPluginData !== undefined && ownPluginData !== null) {
      ownPluginData.get('properties').each(
        function(property) {
          if (property.zoomLevel) {
            this.zoomLevel = Number(1.0)
          }
          if (property.maxFitToScreenLevel) {
            this.maxFitToScreenLevel = Number(property.maxFitToScreenLevel)
          }
          if (property.minZoomLevel) {
            this.minZoomLevel = Number(property.minZoomLevel)
          }
          if (property.maxZoomLevel) {
            this.maxZoomLevel = Number(property.maxZoomLevel)
          }
        }.bind(this)
      )
    }

    /* Register zoom in */
    this.facade.offer({
      name: ORYX.I18N.View.zoomIn,
      functionality: this.zoom.bind(this, [1.0 + ORYX.CONFIG.ZOOM_OFFSET]),
      group: ORYX.I18N.View.group,
      icon: ORYX.PATH + 'images/magnifier_zoom_in.png',
      description: ORYX.I18N.View.zoomInDesc,
      index: 1,
      minShape: 0,
      maxShape: 0,
      isEnabled: function() {
        return this.zoomLevel < this.maxZoomLevel
      }.bind(this)
    })

    /* Register zoom out */
    this.facade.offer({
      name: ORYX.I18N.View.zoomOut,
      functionality: this.zoom.bind(this, [1.0 - ORYX.CONFIG.ZOOM_OFFSET]),
      group: ORYX.I18N.View.group,
      icon: ORYX.PATH + 'images/magnifier_zoom_out.png',
      description: ORYX.I18N.View.zoomOutDesc,
      index: 2,
      minShape: 0,
      maxShape: 0,
      isEnabled: function() {
        return this._checkSize()
      }.bind(this)
    })

    /* Register zoom standard */
    this.facade.offer({
      name: ORYX.I18N.View.zoomStandard,
      functionality: this.setAFixZoomLevel.bind(this, 1),
      group: ORYX.I18N.View.group,
      icon: ORYX.PATH + 'images/zoom_standard.png',
      cls: 'icon-large',
      description: ORYX.I18N.View.zoomStandardDesc,
      index: 3,
      minShape: 0,
      maxShape: 0,
      isEnabled: function() {
        return this.zoomLevel != 1
      }.bind(this)
    })

    /* Register zoom fit to model */
    this.facade.offer({
      name: ORYX.I18N.View.zoomFitToModel,
      functionality: this.zoomFitToModel.bind(this),
      group: ORYX.I18N.View.group,
      icon: ORYX.PATH + 'images/image.png',
      description: ORYX.I18N.View.zoomFitToModelDesc,
      index: 4,
      minShape: 0,
      maxShape: 0
    })
  },

  /**
   * It sets the zoom level to a fix value and call the zooming function.
   *
   * @param {Number} zoomLevel
   * 			the zoom level
   */
  setAFixZoomLevel: function(zoomLevel) {
    this.zoomLevel = zoomLevel
    this._checkZoomLevelRange()
    this.zoom(1)
  },

  /**
   * It does the actual zooming. It changes the viewable size of the canvas
   * and all to its child elements.
   *
   * @param {Number} factor
   * 		the factor to adjust the zoom level
   */
  zoom: function(factor) {
    // TODO: Zoomen auf allen Objekten im SVG-DOM

    this.zoomLevel *= factor
    var scrollNode = this.facade.getCanvas().getHTMLContainer().parentNode.parentNode
    var canvas = this.facade.getCanvas()
    var newWidth = canvas.bounds.width() * this.zoomLevel
    var newHeight = canvas.bounds.height() * this.zoomLevel

    /* Set new top offset */
    var offsetTop = (canvas.node.parentNode.parentNode.parentNode.offsetHeight - newHeight) / 2.0
    offsetTop = offsetTop > 20 ? offsetTop - 20 : 0
    canvas.node.parentNode.parentNode.style.marginTop = offsetTop + 'px'
    offsetTop += 5
    canvas.getHTMLContainer().style.top = offsetTop + 'px'

    /*readjust scrollbar*/
    var newScrollTop =
      scrollNode.scrollTop - Math.round((canvas.getHTMLContainer().parentNode.getHeight() - newHeight) / 2) + this.diff
    var newScrollLeft =
      scrollNode.scrollLeft - Math.round((canvas.getHTMLContainer().parentNode.getWidth() - newWidth) / 2) + this.diff

    /* Set new Zoom-Level */
    canvas.setSize({ width: newWidth, height: newHeight }, true)

    /* Set Scale-Factor */
    canvas.node.setAttributeNS(null, 'transform', 'scale(' + this.zoomLevel + ')')

    /* Refresh the Selection */
    this.facade.updateSelection()
    scrollNode.scrollTop = newScrollTop
    scrollNode.scrollLeft = newScrollLeft

    /* Update the zoom-level*/
    canvas.zoomLevel = this.zoomLevel
  },

  /**
   * It calculates the zoom level to fit whole model into the visible area
   * of the canvas. Than the model gets zoomed and the position of the
   * scroll bars are adjusted.
   *
   */
  zoomFitToModel: function() {
    /* Get the size of the visible area of the canvas */
    var scrollNode = this.facade.getCanvas().getHTMLContainer().parentNode.parentNode
    var visibleHeight = scrollNode.getHeight() - 30
    var visibleWidth = scrollNode.getWidth() - 30

    var nodes = this.facade.getCanvas().getChildShapes()

    if (!nodes || nodes.length < 1) {
      return false
    }

    /* Calculate size of canvas to fit the model */
    var bounds = nodes[0].absoluteBounds().clone()
    nodes.each(function(node) {
      bounds.include(node.absoluteBounds().clone())
    })

    /* Set new Zoom Level */
    var scaleFactorWidth = visibleWidth / bounds.width()
    var scaleFactorHeight = visibleHeight / bounds.height()

    /* Choose the smaller zoom level to fit the whole model */
    var zoomFactor = scaleFactorHeight < scaleFactorWidth ? scaleFactorHeight : scaleFactorWidth

    /*Test if maximum zoom is reached*/
    if (zoomFactor > this.maxFitToScreenLevel) {
      zoomFactor = this.maxFitToScreenLevel
    }
    /* Do zooming */
    this.setAFixZoomLevel(zoomFactor)

    /* Set scroll bar position */
    scrollNode.scrollTop = Math.round(bounds.upperLeft().y * this.zoomLevel) - 5
    scrollNode.scrollLeft = Math.round(bounds.upperLeft().x * this.zoomLevel) - 5
  },

  /**
   * It checks if the zoom level is less or equal to the level, which is required
   * to schow the whole canvas.
   *
   * @private
   */
  _checkSize: function() {
    var canvasParent = this.facade.getCanvas().getHTMLContainer().parentNode
    var minForCanvas = Math.min(
      canvasParent.parentNode.getWidth() / canvasParent.getWidth(),
      canvasParent.parentNode.getHeight() / canvasParent.getHeight()
    )
    return 1.05 > minForCanvas
  },
  /**
   * It checks if the zoom level is included in the definined zoom
   * level range.
   *
   * @private
   */
  _checkZoomLevelRange: function() {
    /*var canvasParent=this.facade.getCanvas().getHTMLContainer().parentNode;
		var maxForCanvas= Math.max((canvasParent.parentNode.getWidth()/canvasParent.getWidth()),(canvasParent.parentNode.getHeight()/canvasParent.getHeight()));
		if(this.zoomLevel > maxForCanvas) {
			this.zoomLevel = maxForCanvas;			
		}*/
    if (this.zoomLevel < this.minZoomLevel) {
      this.zoomLevel = this.minZoomLevel
    }

    if (this.zoomLevel > this.maxZoomLevel) {
      this.zoomLevel = this.maxZoomLevel
    }
  }
}

ORYX.Plugins.View = Clazz.extend(ORYX.Plugins.View)
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

if (!Signavio) {
  var Signavio = {}
}
if (!Signavio.Core) {
  Signavio.Core = {}
}
Signavio.Core.Version = '1.0'
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

if (!Signavio) {
  var Signavio = new Object()
}

if (!Signavio.Plugins) {
  Signavio.Plugins = new Object()
}

if (!Signavio.Plugins.Utils) {
  Signavio.Plugins.Utils = new Object()
}

if (!Signavio.Helper) {
  Signavio.Helper = new Object()
}

new (function() {
  /**
   * Provides an uniq id
   * @overwrite
   * @return {String}
   *
   */
  ORYX.Editor.provideId = function() {
    var res = [],
      hex = '0123456789ABCDEF'

    for (var i = 0; i < 36; i++) res[i] = Math.floor(Math.random() * 0x10)

    res[14] = 4
    res[19] = (res[19] & 0x3) | 0x8

    for (var i = 0; i < 36; i++) res[i] = hex[res[i]]

    res[8] = res[13] = res[18] = res[23] = '-'

    return 'sid-' + res.join('')
  }
})()

/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

if (!ORYX.Plugins) {
  ORYX.Plugins = new Object()
}

/**
 * This plugin is responsible for displaying loading indicators and to prevent
 * the user from accidently unloading the page by, e.g., pressing the backspace
 * button and returning to the previous site in history.
 * @param {Object} facade The editor plugin facade to register enhancements with.
 */
ORYX.Plugins.Loading = {
  construct: function(facade) {
    this.facade = facade

    // The parent Node
    this.node = ORYX.Editor.graft(
      'http://www.w3.org/1999/xhtml',
      this.facade.getCanvas().getHTMLContainer().parentNode,
      [
        'div',
        {
          class: 'LoadingIndicator'
        },
        ''
      ]
    )

    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_LOADING_ENABLE, this.enableLoading.bind(this))
    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_LOADING_DISABLE, this.disableLoading.bind(this))
    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_LOADING_STATUS, this.showStatus.bind(this))

    this.disableLoading()
  },

  enableLoading: function(options) {
    if (options.text) this.node.innerHTML = options.text + '...'
    else this.node.innerHTML = ORYX.I18N.Loading.waiting
    this.node.removeClassName('StatusIndicator')
    this.node.addClassName('LoadingIndicator')
    this.node.style.display = 'block'

    var pos = this.facade.getCanvas().rootNode.parentNode.parentNode.parentNode.parentNode

    this.node.style.top = pos.offsetTop + 'px'
    this.node.style.left = pos.offsetLeft + 'px'
  },

  disableLoading: function() {
    this.node.style.display = 'none'
  },

  showStatus: function(options) {
    if (options.text) {
      this.node.innerHTML = options.text
      this.node.addClassName('StatusIndicator')
      this.node.removeClassName('LoadingIndicator')
      this.node.style.display = 'block'

      var pos = this.facade.getCanvas().rootNode.parentNode.parentNode.parentNode.parentNode

      this.node.style.top = pos.offsetTop + 'px'
      this.node.style.left = pos.offsetLeft + 'px'

      var tout = options.timeout ? options.timeout : 2000

      window.setTimeout(
        function() {
          this.disableLoading()
        }.bind(this),
        tout
      )
    }
  }
}

ORYX.Plugins.Loading = Clazz.extend(ORYX.Plugins.Loading)
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

if (!ORYX.Plugins) {
  ORYX.Plugins = new Object()
}

/**
 * This plugin is responsible for resizing the canvas.
 * @param {Object} facade The editor plugin facade to register enhancements with.
 */
ORYX.Plugins.CanvasResize = Clazz.extend({
  construct: function(facade) {
    this.facade = facade
    new ORYX.Plugins.CanvasResizeButton(this.facade.getCanvas(), 'N', this.resize.bind(this))
    new ORYX.Plugins.CanvasResizeButton(this.facade.getCanvas(), 'W', this.resize.bind(this))
    new ORYX.Plugins.CanvasResizeButton(this.facade.getCanvas(), 'E', this.resize.bind(this))
    new ORYX.Plugins.CanvasResizeButton(this.facade.getCanvas(), 'S', this.resize.bind(this))

    window.setTimeout(function() {
      jQuery(window).trigger('resize')
    })
  },

  resize: function(position, shrink) {
    resizeCanvas = function(position, extentionSize, facade) {
      var canvas = facade.getCanvas()
      var b = canvas.bounds
      var scrollNode = facade.getCanvas().getHTMLContainer().parentNode.parentNode

      if (position == 'E' || position == 'W') {
        canvas.setSize({ width: (b.width() + extentionSize) * canvas.zoomLevel, height: b.height() * canvas.zoomLevel })
      } else if (position == 'S' || position == 'N') {
        canvas.setSize({ width: b.width() * canvas.zoomLevel, height: (b.height() + extentionSize) * canvas.zoomLevel })
      }

      if (position == 'N' || position == 'W') {
        var move = position == 'N' ? { x: 0, y: extentionSize } : { x: extentionSize, y: 0 }

        // Move all children
        canvas.getChildNodes(false, function(shape) {
          shape.bounds.moveBy(move)
        })
        // Move all dockers, when the edge has at least one docked shape
        var edges = canvas.getChildEdges().findAll(function(edge) {
          return edge.getAllDockedShapes().length > 0
        })
        var dockers = edges
          .collect(function(edge) {
            return edge.dockers.findAll(function(docker) {
              return !docker.getDockedShape()
            })
          })
          .flatten()
        dockers.each(function(docker) {
          docker.bounds.moveBy(move)
        })
      } else if (position == 'S') {
        scrollNode.scrollTop += extentionSize
      } else if (position == 'E') {
        scrollNode.scrollLeft += extentionSize
      }

      jQuery(window).trigger('resize')

      canvas.update()
      facade.updateSelection()
    }

    var commandClass = ORYX.Core.Command.extend({
      construct: function(position, extentionSize, facade) {
        this.position = position
        this.extentionSize = extentionSize
        this.facade = facade
      },
      execute: function() {
        resizeCanvas(this.position, this.extentionSize, this.facade)
      },
      rollback: function() {
        resizeCanvas(this.position, -this.extentionSize, this.facade)
      },
      update: function() {}
    })

    var extentionSize = ORYX.CONFIG.CANVAS_RESIZE_INTERVAL
    if (shrink) extentionSize = -extentionSize
    var command = new commandClass(position, extentionSize, this.facade)

    this.facade.executeCommands([command])
  }
})

ORYX.Plugins.CanvasResizeButton = Clazz.extend({
  construct: function(canvas, position, callback) {
    this.canvas = canvas
    var parentNode = canvas.getHTMLContainer().parentNode

    window.myParent = parentNode

    var actualScrollNode = jQuery('#canvasSection')[0]
    var scrollNode = actualScrollNode
    var canvasNode = $$('#canvasSection .ORYX_Editor')[0]
    var svgRootNode = canvasNode.children[0]

    var iconClass = 'glyphicon glyphicon-chevron-'
    var iconClassShrink = 'glyphicon glyphicon-chevron-'
    if (position == 'N') {
      iconClass += 'up'
      iconClassShrink += 'down'
    } else if (position == 'S') {
      iconClass += 'down'
      iconClassShrink += 'up'
    } else if (position == 'E') {
      iconClass += 'right'
      iconClassShrink += 'left'
    } else if (position == 'W') {
      iconClass += 'left'
      iconClassShrink += 'right'
    }

    // The buttons
    var idGrow = 'canvas-shrink-' + position
    var idShrink = 'canvas-grow-' + position

    var buttonGrow = ORYX.Editor.graft('http://www.w3.org/1999/xhtml', parentNode, [
      'div',
      {
        class: 'canvas_resize_indicator canvas_resize_indicator_grow' + ' ' + position,
        id: idGrow,
        title: ORYX.I18N.RESIZE.tipGrow + ORYX.I18N.RESIZE[position]
      },
      ['i', { class: iconClass }]
    ])
    var buttonShrink = ORYX.Editor.graft('http://www.w3.org/1999/xhtml', parentNode, [
      'div',
      {
        class: 'canvas_resize_indicator canvas_resize_indicator_shrink' + ' ' + position,
        id: idShrink,
        title: ORYX.I18N.RESIZE.tipGrow + ORYX.I18N.RESIZE[position]
      },
      ['i', { class: iconClassShrink }]
    ])
    // Defines a callback which gives back
    // a boolean if the current mouse event
    // is over the particular button area
    var offSetWidth = 60
    var isOverOffset = function(event) {
      var isOverButton =
        event.target.id.indexOf('canvas-shrink') != -1 ||
        event.target.id.indexOf('canvas-grow') != -1 ||
        event.target.parentNode.id.indexOf('canvas-shrink') != -1 ||
        event.target.parentNode.id.indexOf('canvas-grow') != -1
      if (isOverButton) {
        if (
          event.target.id == idGrow ||
          event.target.id == idShrink ||
          event.target.parentNode.id == idGrow ||
          event.target.parentNode.id == idShrink
        ) {
          return true
        } else {
          return false
        }
      }

      if (
        event.target != parentNode &&
        event.target != scrollNode &&
        event.target != scrollNode.firstChild &&
        event.target != svgRootNode &&
        event.target != scrollNode
      ) {
        return false
      }

      //if(inCanvas){offSetWidth=30}else{offSetWidth=30*2}
      //Safari work around
      var X = event.offsetX !== undefined ? event.offsetX : event.layerX
      var Y = event.offsetY !== undefined ? event.offsetY : event.layerY

      var canvasOffset = 0
      if (canvasNode.clientWidth < actualScrollNode.clientWidth) {
        var widthDiff = actualScrollNode.clientWidth - canvasNode.clientWidth
        canvasOffset = widthDiff / 2
      }

      // Adjust to relative location to the actual viewport
      Y = Y - actualScrollNode.scrollTop
      X = X - actualScrollNode.scrollLeft

      if (position == 'N') {
        return Y < offSetWidth
      } else if (position == 'W') {
        return X < offSetWidth + canvasOffset
      } else if (position == 'E') {
        return actualScrollNode.clientWidth - X < offSetWidth + canvasOffset
      } else if (position == 'S') {
        return actualScrollNode.clientHeight - Y < offSetWidth
      }

      return false
    }

    var showButtons = function() {
      buttonGrow.show()

      var w = canvas.bounds.width()
      var h = canvas.bounds.height()

      if (position == 'N' && h - ORYX.CONFIG.CANVAS_RESIZE_INTERVAL > ORYX.CONFIG.CANVAS_MIN_HEIGHT) buttonShrink.show()
      else if (position == 'E' && w - ORYX.CONFIG.CANVAS_RESIZE_INTERVAL > ORYX.CONFIG.CANVAS_MIN_WIDTH)
        buttonShrink.show()
      else if (position == 'S' && h - ORYX.CONFIG.CANVAS_RESIZE_INTERVAL > ORYX.CONFIG.CANVAS_MIN_HEIGHT)
        buttonShrink.show()
      else if (position == 'W' && w - ORYX.CONFIG.CANVAS_RESIZE_INTERVAL > ORYX.CONFIG.CANVAS_MIN_WIDTH)
        buttonShrink.show()
      else buttonShrink.hide()
    }.bind(this)

    var hideButtons = function() {
      buttonGrow.hide()
      buttonShrink.hide()
    }

    // If the mouse move is over the button area, show the button
    parentNode.parentNode.addEventListener(
      ORYX.CONFIG.EVENT_MOUSEMOVE,
      function(event) {
        if (isOverOffset(event)) {
          showButtons()
        } else {
          hideButtons()
        }
      },
      false
    )
    // If the mouse is over the button, show them
    buttonGrow.addEventListener(
      ORYX.CONFIG.EVENT_MOUSEOVER,
      function(event) {
        showButtons()
      },
      true
    )
    buttonShrink.addEventListener(
      ORYX.CONFIG.EVENT_MOUSEOVER,
      function(event) {
        showButtons()
      },
      true
    )
    // If the mouse is out, hide the button
    //scrollNode.addEventListener(		ORYX.CONFIG.EVENT_MOUSEOUT, 	function(event){button.hide()}, true )
    parentNode.parentNode.addEventListener(
      ORYX.CONFIG.EVENT_MOUSEOUT,
      function(event) {
        hideButtons()
      },
      true
    )
    //svgRootNode.addEventListener(	ORYX.CONFIG.EVENT_MOUSEOUT, 	function(event){ inCanvas = false } , true );

    // Hide the button initialy
    hideButtons()

    // Add the callbacks
    buttonGrow.addEventListener(
      'click',
      function() {
        callback(position)
        showButtons()
      },
      true
    )
    buttonShrink.addEventListener(
      'click',
      function() {
        callback(position, true)
        showButtons()
      },
      true
    )
  }
})

/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

if (!ORYX.Plugins) ORYX.Plugins = new Object()

ORYX.Plugins.RenameShapes = Clazz.extend({
  facade: undefined,

  construct: function(facade) {
    this.facade = facade

    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_CANVAS_SCROLL, this.hideField.bind(this))
    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_DBLCLICK, this.actOnDBLClick.bind(this))
    this.facade.offer({
      keyCodes: [
        {
          keyCode: 113, // F2-Key
          keyAction: ORYX.CONFIG.KEY_ACTION_DOWN
        }
      ],
      functionality: this.renamePerF2.bind(this)
    })

    document.documentElement.addEventListener(ORYX.CONFIG.EVENT_MOUSEDOWN, this.hide.bind(this), true)
  },

  /**
   * This method handles the "F2" key down event. The selected shape are looked
   * up and the editing of title/name of it gets started.
   */
  renamePerF2: function() {
    var selectedShapes = this.facade.getSelection()
    this.actOnDBLClick(undefined, selectedShapes.first())
  },

  actOnDBLClick: function(evt, shape) {
    if (!(shape instanceof ORYX.Core.Shape)) {
      return
    }

    // Destroys the old input, if there is one
    this.destroy()

    // Get all properties which where at least one ref to view is set
    var props = shape
      .getStencil()
      .properties()
      .findAll(function(item) {
        return item.refToView() && item.refToView().length > 0 && item.directlyEditable()
      })
    // from these, get all properties where write access are and the type is String or Expression
    props = props.findAll(function(item) {
      return (
        !item.readonly() &&
        (item.type() == ORYX.CONFIG.TYPE_STRING ||
          item.type() == ORYX.CONFIG.TYPE_EXPRESSION ||
          item.type() == ORYX.CONFIG.TYPE_DATASOURCE)
      )
    })

    // Get all ref ids
    var allRefToViews = props
      .collect(function(prop) {
        return prop.refToView()
      })
      .flatten()
      .compact()
    // Get all labels from the shape with the ref ids
    var labels = shape.getLabels().findAll(function(label) {
      return allRefToViews.any(function(toView) {
        return label.id.endsWith(toView)
      })
    })

    // If there are no referenced labels --> return
    if (labels.length == 0) {
      return
    }

    // Define the nearest label
    var nearestLabel = labels.length <= 1 ? labels[0] : null
    if (!nearestLabel) {
      nearestLabel = labels.find(function(label) {
        return label.node == evt.target || label.node == evt.target.parentNode
      })
      if (!nearestLabel) {
        var evtCoord = this.facade.eventCoordinates(evt)

        var additionalIEZoom = 1
        if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) {
          var ua = navigator.userAgent
          if (ua.indexOf('MSIE') >= 0) {
            //IE 10 and below
            var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100)
            if (zoom !== 100) {
              additionalIEZoom = zoom / 100
            }
          }
        }

        if (additionalIEZoom !== 1) {
          evtCoord.x = evtCoord.x / additionalIEZoom
          evtCoord.y = evtCoord.y / additionalIEZoom
        }

        evtCoord.y += $('editor-header').clientHeight - $('canvasSection').scrollTop - 5
        if (FLOWABLE.HEADER_CONFIG.showAppTitle == false) {
          evtCoord.y += 61
        }

        evtCoord.x -= $('canvasSection').scrollLeft

        var trans = this.facade.getCanvas().rootNode.lastChild.getScreenCTM()
        evtCoord.x *= trans.a
        evtCoord.y *= trans.d

        var diff = labels.collect(
          function(label) {
            var center = this.getCenterPosition(label.node)
            var len = Math.sqrt(Math.pow(center.x - evtCoord.x, 2) + Math.pow(center.y - evtCoord.y, 2))
            return { diff: len, label: label }
          }.bind(this)
        )

        diff.sort(function(a, b) {
          return a.diff > b.diff
        })

        nearestLabel = diff[0].label
      }
    }
    // Get the particular property for the label
    var prop = props.find(function(item) {
      return item.refToView().any(function(toView) {
        return nearestLabel.id == shape.id + toView
      })
    })

    // Get the center position from the nearest label
    var width = Math.min(Math.max(100, shape.bounds.width()), 200)
    var center = this.getCenterPosition(nearestLabel.node, shape)
    center.x -= width / 2
    var propId = prop.prefix() + '-' + prop.id()
    var textInput = document.createElement('textarea')
    textInput.id = 'shapeTextInput'
    textInput.style.position = 'absolute'
    textInput.style.width = width + 'px'
    textInput.style.left = center.x < 10 ? 10 : center.x + 'px'
    textInput.style.top = center.y - 15 + 'px'
    textInput.className = 'x-form-textarea x-form-field x_form_text_set_absolute'
    textInput.value = shape.properties.get(propId)
    this.oldValueText = shape.properties.get(propId)
    document.getElementById('canvasSection').appendChild(textInput)
    this.shownTextField = textInput

    // Value change listener needs to be defined now since we reference it in the text field
    this.updateValueFunction = function(newValue, oldValue) {
      var currentEl = shape
      var facade = this.facade

      if (oldValue != newValue) {
        // Implement the specific command for property change
        var commandClass = ORYX.Core.Command.extend({
          construct: function() {
            this.el = currentEl
            this.propId = propId
            this.oldValue = oldValue
            this.newValue = newValue
            this.facade = facade
          },
          execute: function() {
            this.el.setProperty(this.propId, this.newValue)
            //this.el.update();
            this.facade.setSelection([this.el])
            this.facade.getCanvas().update()
            this.facade.updateSelection()
          },
          rollback: function() {
            this.el.setProperty(this.propId, this.oldValue)
            //this.el.update();
            this.facade.setSelection([this.el])
            this.facade.getCanvas().update()
            this.facade.updateSelection()
          }
        })
        // Instantiated the class
        var command = new commandClass()

        // Execute the command
        this.facade.executeCommands([command])
      }
    }.bind(this)

    jQuery('#shapeTextInput').focus()

    jQuery('#shapeTextInput').autogrow()

    // Disable the keydown in the editor (that when hitting the delete button, the shapes not get deleted)
    this.facade.disableEvent(ORYX.CONFIG.EVENT_KEYDOWN)
  },

  getCenterPosition: function(svgNode, shape) {
    if (!svgNode) {
      return { x: 0, y: 0 }
    }

    var scale = this.facade.getCanvas().node.getScreenCTM()
    var absoluteXY = shape.bounds.upperLeft()

    var hasParent = true
    var searchShape = shape
    while (hasParent) {
      if (
        searchShape
          .getParentShape()
          .getStencil()
          .idWithoutNs() === 'BPMNDiagram' ||
        searchShape
          .getParentShape()
          .getStencil()
          .idWithoutNs() === 'CMMNDiagram'
      ) {
        hasParent = false
      } else {
        var parentXY = searchShape.getParentShape().bounds.upperLeft()
        absoluteXY.x += parentXY.x
        absoluteXY.y += parentXY.y
        searchShape = searchShape.getParentShape()
      }
    }

    var center = shape.bounds.midPoint()
    center.x += absoluteXY.x + scale.e
    center.y += absoluteXY.y + scale.f

    center.x *= scale.a
    center.y *= scale.d

    var additionalIEZoom = 1
    if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) {
      var ua = navigator.userAgent
      if (ua.indexOf('MSIE') >= 0) {
        //IE 10 and below
        var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100)
        if (zoom !== 100) {
          additionalIEZoom = zoom / 100
        }
      }
    }

    if (additionalIEZoom === 1) {
      center.y = center.y - jQuery('#canvasSection').offset().top + 5
      center.x -= jQuery('#canvasSection').offset().left
    } else {
      var canvasOffsetLeft = jQuery('#canvasSection').offset().left
      var canvasScrollLeft = jQuery('#canvasSection').scrollLeft()
      var canvasScrollTop = jQuery('#canvasSection').scrollTop()

      var offset = scale.e - canvasOffsetLeft * additionalIEZoom
      var additionaloffset = 0
      if (offset > 10) {
        additionaloffset = offset / additionalIEZoom - offset
      }
      center.y =
        center.y -
        jQuery('#canvasSection').offset().top * additionalIEZoom +
        5 +
        (canvasScrollTop * additionalIEZoom - canvasScrollTop)
      center.x =
        center.x -
        canvasOffsetLeft * additionalIEZoom +
        additionaloffset +
        (canvasScrollLeft * additionalIEZoom - canvasScrollLeft)
    }

    return center
  },

  hide: function(e) {
    if (this.shownTextField && (!e || e.target !== this.shownTextField)) {
      var newValue = this.shownTextField.value
      if (newValue !== this.oldValueText) {
        this.updateValueFunction(newValue, this.oldValueText)
      }
      this.destroy()
    }
  },

  hideField: function(e) {
    if (this.shownTextField) {
      this.destroy()
    }
  },

  destroy: function(e) {
    var textInputComp = jQuery('#shapeTextInput')
    if (textInputComp) {
      textInputComp.remove()
      delete this.shownTextField

      this.facade.enableEvent(ORYX.CONFIG.EVENT_KEYDOWN)
    }
  }
})
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

if (!ORYX.Plugins) ORYX.Plugins = new Object()

/**
 * Supports EPCs by offering a syntax check and export and import ability..
 *
 *
 */
ORYX.Plugins.ProcessLink = Clazz.extend({
  facade: undefined,

  /**
   * Offers the plugin functionality:
   *
   */
  construct: function(facade) {
    this.facade = facade

    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_PROPERTY_CHANGED, this.propertyChanged.bind(this))
  },

  /**
   *
   * @param {Object} option
   */
  propertyChanged: function(option, node) {
    if (option.name !== 'oryx-refuri' || !node instanceof ORYX.Core.Node) {
      return
    }

    if (option.value && option.value.length > 0 && option.value != 'undefined') {
      this.show(node, option.value)
    } else {
      this.hide(node)
    }
  },

  /**
   * Shows the Link for a particular shape with a specific url
   *
   * @param {Object} shape
   * @param {Object} url
   */
  show: function(shape, url) {
    // Generate the svg-representation of a link
    var link = ORYX.Editor.graft('http://www.w3.org/2000/svg', null, [
      'a',
      { target: '_blank' },
      [
        'path',
        {
          'stroke-width': 1.0,
          stroke: '#00DD00',
          fill: '#00AA00',
          d: 'M3,3 l0,-2.5 l7.5,0 l0,-2.5 l7.5,4.5 l-7.5,3.5 l0,-2.5 l-8,0',
          'line-captions': 'round'
        }
      ]
    ])

    var link = ORYX.Editor.graft('http://www.w3.org/2000/svg', null, [
      'a',
      { target: '_blank' },
      [
        'path',
        {
          style: 'fill:#92BFFC;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.72',
          d: 'M0 1.44 L0 15.05 L11.91 15.05 L11.91 5.98 L7.37 1.44 L0 1.44 Z'
        }
      ],
      [
        'path',
        {
          style: 'stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.72;fill:none;',
          transform: 'translate(7.5, -8.5)',
          d: 'M0 10.51 L0 15.05 L4.54 15.05'
        }
      ],
      [
        'path',
        {
          style: 'fill:#f28226;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.72',
          transform: 'translate(-3, -1)',
          d:
            'M0 8.81 L0 13.06 L5.95 13.06 L5.95 15.05 A50.2313 50.2313 -175.57 0 0 10.77 11.08 A49.9128 49.9128 -1.28 0 0 5.95 6.54 L5.95 8.81 L0 8.81 Z'
        }
      ]
    ])

    /*
	 * 
	 * 					[ 'a',
						{'target': '_blank'},
						['path', { "style": "fill:none;stroke-width:0.5px; stroke:#000000", "d": "M7,4 l0,2"}],
						['path', { "style": "fill:none;stroke-width:0.5px; stroke:#000000", "d": "M4,8 l-2,0 l0,6"}],
						['path', { "style": "fill:none;stroke-width:0.5px; stroke:#000000", "d": "M10,8 l2,0 l0,6"}],
						['rect', { "style": "fill:#96ff96;stroke:#000000;stroke-width:1", "width": 6, "height": 4, "x": 4, "y": 0}],
						['rect', { "style": "fill:#ffafff;stroke:#000000;stroke-width:1", "width": 6, "height": 4, "x": 4, "y": 6}],
						['rect', { "style": "fill:#96ff96;stroke:#000000;stroke-width:1", "width": 6, "height": 4, "x": 0, "y": 12}],
						['rect', { "style": "fill:#96ff96;stroke:#000000;stroke-width:1", "width": 6, "height": 4, "x": 8, "y": 12}],
						['rect', { "style": "fill:none;stroke:none;pointer-events:all", "width": 14, "height": 16, "x": 0, "y": 0}]
					]);
	 */

    // Set the link with the special namespace
    link.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', url)

    // Shows the link in the overlay
    this.facade.raiseEvent({
      type: ORYX.CONFIG.EVENT_OVERLAY_SHOW,
      id: 'arissupport.urlref_' + shape.id,
      shapes: [shape],
      node: link,
      nodePosition: 'SE'
    })
  },

  /**
   * Hides the Link for a particular shape
   *
   * @param {Object} shape
   */
  hide: function(shape) {
    this.facade.raiseEvent({
      type: ORYX.CONFIG.EVENT_OVERLAY_HIDE,
      id: 'arissupport.urlref_' + shape.id
    })
  }
})
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

Array.prototype.insertFrom = function(from, to) {
  to = Math.max(0, to)
  from = Math.min(Math.max(0, from), this.length - 1)

  var el = this[from]
  var old = this.without(el)
  var newA = old.slice(0, to)
  newA.push(el)
  if (old.length > to) {
    newA = newA.concat(old.slice(to))
  }
  return newA
}

if (!ORYX.Plugins) ORYX.Plugins = new Object()

ORYX.Plugins.Arrangement = ORYX.Plugins.AbstractPlugin.extend({
  facade: undefined,

  construct: function(facade) {
    this.facade = facade

    // Z-Ordering
    /** Hide for SIGNAVIO 
		
		this.facade.offer({
			'name':ORYX.I18N.Arrangement.btf,
			'functionality': this.setZLevel.bind(this, this.setToTop),
			'group': ORYX.I18N.Arrangement.groupZ,
			'icon': ORYX.PATH + "images/shape_move_front.png",
			'description': ORYX.I18N.Arrangement.btfDesc,
			'index': 1,
			'minShape': 1});
			
		this.facade.offer({
			'name':ORYX.I18N.Arrangement.btb,
			'functionality': this.setZLevel.bind(this, this.setToBack),
			'group': ORYX.I18N.Arrangement.groupZ,
			'icon': ORYX.PATH + "images/shape_move_back.png",
			'description': ORYX.I18N.Arrangement.btbDesc,
			'index': 2,
			'minShape': 1});

		this.facade.offer({
			'name':ORYX.I18N.Arrangement.bf,
			'functionality': this.setZLevel.bind(this, this.setForward),
			'group': ORYX.I18N.Arrangement.groupZ,
			'icon': ORYX.PATH + "images/shape_move_forwards.png",
			'description': ORYX.I18N.Arrangement.bfDesc,
			'index': 3,
			'minShape': 1});

		this.facade.offer({
			'name':ORYX.I18N.Arrangement.bb,
			'functionality': this.setZLevel.bind(this, this.setBackward),
			'group': ORYX.I18N.Arrangement.groupZ,
			'icon': ORYX.PATH + "images/shape_move_backwards.png",
			'description': ORYX.I18N.Arrangement.bbDesc,
			'index': 4,
			'minShape': 1});

		// Aligment
		this.facade.offer({
			'name':ORYX.I18N.Arrangement.ab,
			'functionality': this.alignShapes.bind(this, [ORYX.CONFIG.EDITOR_ALIGN_BOTTOM]),
			'group': ORYX.I18N.Arrangement.groupA,
			'icon': ORYX.PATH + "images/shape_align_bottom.png",
			'description': ORYX.I18N.Arrangement.abDesc,
			'index': 1,
			'minShape': 2});



		this.facade.offer({
			'name':ORYX.I18N.Arrangement.at,
			'functionality': this.alignShapes.bind(this, [ORYX.CONFIG.EDITOR_ALIGN_TOP]),
			'group': ORYX.I18N.Arrangement.groupA,
			'icon': ORYX.PATH + "images/shape_align_top.png",
			'description': ORYX.I18N.Arrangement.atDesc,
			'index': 3,
			'minShape': 2});

		this.facade.offer({
			'name':ORYX.I18N.Arrangement.al,
			'functionality': this.alignShapes.bind(this, [ORYX.CONFIG.EDITOR_ALIGN_LEFT]),
			'group': ORYX.I18N.Arrangement.groupA,
			'icon': ORYX.PATH + "images/shape_align_left.png",
			'description': ORYX.I18N.Arrangement.alDesc,
			'index': 4,
			'minShape': 2});

		this.facade.offer({
			'name':ORYX.I18N.Arrangement.ar,
			'functionality': this.alignShapes.bind(this, [ORYX.CONFIG.EDITOR_ALIGN_RIGHT]),
			'group': ORYX.I18N.Arrangement.groupA,
			'icon': ORYX.PATH + "images/shape_align_right.png",
			'description': ORYX.I18N.Arrangement.arDesc,
			'index': 6,
			'minShape': 2});

		**/

    this.facade.offer({
      name: ORYX.I18N.Arrangement.am,
      functionality: this.alignShapes.bind(this, [ORYX.CONFIG.EDITOR_ALIGN_MIDDLE]),
      group: ORYX.I18N.Arrangement.groupA,
      icon: ORYX.PATH + 'images/shape_align_middle.png',
      description: ORYX.I18N.Arrangement.amDesc,
      index: 1,
      minShape: 2
    })

    this.facade.offer({
      name: ORYX.I18N.Arrangement.ac,
      functionality: this.alignShapes.bind(this, [ORYX.CONFIG.EDITOR_ALIGN_CENTER]),
      group: ORYX.I18N.Arrangement.groupA,
      icon: ORYX.PATH + 'images/shape_align_center.png',
      description: ORYX.I18N.Arrangement.acDesc,
      index: 2,
      minShape: 2
    })

    this.facade.offer({
      name: ORYX.I18N.Arrangement.as,
      functionality: this.alignShapes.bind(this, [ORYX.CONFIG.EDITOR_ALIGN_SIZE]),
      group: ORYX.I18N.Arrangement.groupA,
      icon: ORYX.PATH + 'images/shape_align_size.png',
      description: ORYX.I18N.Arrangement.asDesc,
      index: 3,
      minShape: 2
    })

    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_ARRANGEMENT_TOP, this.setZLevel.bind(this, this.setToTop))
    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_ARRANGEMENT_BACK, this.setZLevel.bind(this, this.setToBack))
    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_ARRANGEMENT_FORWARD, this.setZLevel.bind(this, this.setForward))
    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_ARRANGEMENT_BACKWARD, this.setZLevel.bind(this, this.setBackward))
  },

  onSelectionChanged: function(elemnt) {
    var selection = this.facade.getSelection()
    if (selection.length === 1 && selection[0] instanceof ORYX.Core.Edge) {
      this.setToTop(selection)
    }
  },

  setZLevel: function(callback, event) {
    //Command-Pattern for dragging one docker
    var zLevelCommand = ORYX.Core.Command.extend({
      construct: function(callback, elements, facade) {
        this.callback = callback
        this.elements = elements
        // For redo, the previous elements get stored
        this.elAndIndex = elements.map(function(el) {
          return { el: el, previous: el.parent.children[el.parent.children.indexOf(el) - 1] }
        })
        this.facade = facade
      },
      execute: function() {
        // Call the defined z-order callback with the elements
        this.callback(this.elements)
        this.facade.setSelection(this.elements)
      },
      rollback: function() {
        // Sort all elements on the index of there containment
        var sortedEl = this.elAndIndex.sortBy(function(el) {
          var value = el.el
          var t = $A(value.node.parentNode.childNodes)
          return t.indexOf(value.node)
        })

        // Every element get setted back bevor the old previous element
        for (var i = 0; i < sortedEl.length; i++) {
          var el = sortedEl[i].el
          var p = el.parent
          var oldIndex = p.children.indexOf(el)
          var newIndex = p.children.indexOf(sortedEl[i].previous)
          newIndex = newIndex || 0
          p.children = p.children.insertFrom(oldIndex, newIndex)
          el.node.parentNode.insertBefore(el.node, el.node.parentNode.childNodes[newIndex + 1])
        }

        // Reset the selection
        this.facade.setSelection(this.elements)
      }
    })

    // Instanziate the dockCommand
    var command = new zLevelCommand(callback, this.facade.getSelection(), this.facade)
    if (event.excludeCommand) {
      command.execute()
    } else {
      this.facade.executeCommands([command])
    }
  },

  setToTop: function(elements) {
    // Sortieren des Arrays nach dem Index des SVGKnotens im Bezug auf dem Elternknoten.
    var tmpElem = elements.sortBy(function(value, index) {
      var t = $A(value.node.parentNode.childNodes)
      return t.indexOf(value.node)
    })
    // Sortiertes Array wird nach oben verschoben.
    tmpElem.each(function(value) {
      var p = value.parent
      if (p.children.last() === value) {
        return
      }
      p.children = p.children.without(value)
      p.children.push(value)
      value.node.parentNode.appendChild(value.node)
    })
  },

  setToBack: function(elements) {
    // Sortieren des Arrays nach dem Index des SVGKnotens im Bezug auf dem Elternknoten.
    var tmpElem = elements.sortBy(function(value, index) {
      var t = $A(value.node.parentNode.childNodes)
      return t.indexOf(value.node)
    })

    tmpElem = tmpElem.reverse()

    // Sortiertes Array wird nach unten verschoben.
    tmpElem.each(function(value) {
      var p = value.parent
      p.children = p.children.without(value)
      p.children.unshift(value)
      value.node.parentNode.insertBefore(value.node, value.node.parentNode.firstChild)
    })
  },

  setBackward: function(elements) {
    // Sortieren des Arrays nach dem Index des SVGKnotens im Bezug auf dem Elternknoten.
    var tmpElem = elements.sortBy(function(value, index) {
      var t = $A(value.node.parentNode.childNodes)
      return t.indexOf(value.node)
    })

    // Reverse the elements
    tmpElem = tmpElem.reverse()

    // Delete all Nodes who are the next Node in the nodes-Array
    var compactElem = tmpElem.findAll(function(el) {
      return !tmpElem.some(function(checkedEl) {
        return checkedEl.node == el.node.previousSibling
      })
    })

    // Sortiertes Array wird nach eine Ebene nach oben verschoben.
    compactElem.each(function(el) {
      if (el.node.previousSibling === null) {
        return
      }
      var p = el.parent
      var index = p.children.indexOf(el)
      p.children = p.children.insertFrom(index, index - 1)
      el.node.parentNode.insertBefore(el.node, el.node.previousSibling)
    })
  },

  setForward: function(elements) {
    // Sortieren des Arrays nach dem Index des SVGKnotens im Bezug auf dem Elternknoten.
    var tmpElem = elements.sortBy(function(value, index) {
      var t = $A(value.node.parentNode.childNodes)
      return t.indexOf(value.node)
    })

    // Delete all Nodes who are the next Node in the nodes-Array
    var compactElem = tmpElem.findAll(function(el) {
      return !tmpElem.some(function(checkedEl) {
        return checkedEl.node == el.node.nextSibling
      })
    })

    // Sortiertes Array wird eine Ebene nach unten verschoben.
    compactElem.each(function(el) {
      var nextNode = el.node.nextSibling
      if (nextNode === null) {
        return
      }
      var index = el.parent.children.indexOf(el)
      var p = el.parent
      p.children = p.children.insertFrom(index, index + 1)
      el.node.parentNode.insertBefore(nextNode, el.node)
    })
  },

  alignShapes: function(way) {
    var elements = this.facade.getSelection()

    // Set the elements to all Top-Level elements
    elements = this.facade.getCanvas().getShapesWithSharedParent(elements)
    // Get only nodes
    elements = elements.findAll(function(value) {
      return value instanceof ORYX.Core.Node
    })
    // Delete all attached intermediate events from the array
    elements = elements.findAll(function(value) {
      var d = value.getIncomingShapes()
      return d.length == 0 || !elements.include(d[0])
    })
    if (elements.length < 2) {
      return
    }

    // get bounds of all shapes.
    var bounds = elements[0].absoluteBounds().clone()
    elements.each(function(shape) {
      bounds.include(shape.absoluteBounds().clone())
    })

    // get biggest width and heigth
    var maxWidth = 0
    var maxHeight = 0
    elements.each(function(shape) {
      maxWidth = Math.max(shape.bounds.width(), maxWidth)
      maxHeight = Math.max(shape.bounds.height(), maxHeight)
    })

    var commandClass = ORYX.Core.Command.extend({
      construct: function(elements, bounds, maxHeight, maxWidth, way, plugin) {
        this.elements = elements
        this.bounds = bounds
        this.maxHeight = maxHeight
        this.maxWidth = maxWidth
        this.way = way
        this.facade = plugin.facade
        this.plugin = plugin
        this.orgPos = []
      },
      setBounds: function(shape, maxSize) {
        if (!maxSize) maxSize = { width: ORYX.CONFIG.MAXIMUM_SIZE, height: ORYX.CONFIG.MAXIMUM_SIZE }

        if (!shape.bounds) {
          throw 'Bounds not definined.'
        }

        var newBounds = {
          a: {
            x: shape.bounds.upperLeft().x - (this.maxWidth - shape.bounds.width()) / 2,
            y: shape.bounds.upperLeft().y - (this.maxHeight - shape.bounds.height()) / 2
          },
          b: {
            x: shape.bounds.lowerRight().x + (this.maxWidth - shape.bounds.width()) / 2,
            y: shape.bounds.lowerRight().y + (this.maxHeight - shape.bounds.height()) / 2
          }
        }

        /* If the new width of shape exceeds the maximum width, set width value to maximum. */
        if (this.maxWidth > maxSize.width) {
          newBounds.a.x = shape.bounds.upperLeft().x - (maxSize.width - shape.bounds.width()) / 2

          newBounds.b.x = shape.bounds.lowerRight().x + (maxSize.width - shape.bounds.width()) / 2
        }

        /* If the new height of shape exceeds the maximum height, set height value to maximum. */
        if (this.maxHeight > maxSize.height) {
          newBounds.a.y = shape.bounds.upperLeft().y - (maxSize.height - shape.bounds.height()) / 2

          newBounds.b.y = shape.bounds.lowerRight().y + (maxSize.height - shape.bounds.height()) / 2
        }

        /* set bounds of shape */
        shape.bounds.set(newBounds)
      },
      execute: function() {
        // align each shape according to the way that was specified.
        this.elements.each(
          function(shape, index) {
            this.orgPos[index] = shape.bounds.upperLeft()

            var relBounds = this.bounds.clone()
            var newCoordinates
            if (shape.parent && !(shape.parent instanceof ORYX.Core.Canvas)) {
              var upL = shape.parent.absoluteBounds().upperLeft()
              relBounds.moveBy(-upL.x, -upL.y)
            }

            switch (this.way) {
              // align the shapes in the requested way.
              case ORYX.CONFIG.EDITOR_ALIGN_BOTTOM:
                newCoordinates = {
                  x: shape.bounds.upperLeft().x,
                  y: relBounds.b.y - shape.bounds.height()
                }
                break

              case ORYX.CONFIG.EDITOR_ALIGN_MIDDLE:
                newCoordinates = {
                  x: shape.bounds.upperLeft().x,
                  y: (relBounds.a.y + relBounds.b.y - shape.bounds.height()) / 2
                }
                break

              case ORYX.CONFIG.EDITOR_ALIGN_TOP:
                newCoordinates = {
                  x: shape.bounds.upperLeft().x,
                  y: relBounds.a.y
                }
                break

              case ORYX.CONFIG.EDITOR_ALIGN_LEFT:
                newCoordinates = {
                  x: relBounds.a.x,
                  y: shape.bounds.upperLeft().y
                }
                break

              case ORYX.CONFIG.EDITOR_ALIGN_CENTER:
                newCoordinates = {
                  x: (relBounds.a.x + relBounds.b.x - shape.bounds.width()) / 2,
                  y: shape.bounds.upperLeft().y
                }
                break

              case ORYX.CONFIG.EDITOR_ALIGN_RIGHT:
                newCoordinates = {
                  x: relBounds.b.x - shape.bounds.width(),
                  y: shape.bounds.upperLeft().y
                }
                break

              case ORYX.CONFIG.EDITOR_ALIGN_SIZE:
                if (shape.isResizable) {
                  this.orgPos[index] = { a: shape.bounds.upperLeft(), b: shape.bounds.lowerRight() }
                  this.setBounds(shape, shape.maximumSize)
                }
                break
            }

            if (newCoordinates) {
              var offset = {
                x: shape.bounds.upperLeft().x - newCoordinates.x,
                y: shape.bounds.upperLeft().y - newCoordinates.y
              }
              // Set the new position
              shape.bounds.moveTo(newCoordinates)
              this.plugin.layoutEdges(shape, shape.getAllDockedShapes(), offset)
              //shape.update()
            }
          }.bind(this)
        )

        //this.facade.getCanvas().update();
        //this.facade.updateSelection();
      },
      rollback: function() {
        this.elements.each(
          function(shape, index) {
            if (this.way == ORYX.CONFIG.EDITOR_ALIGN_SIZE) {
              if (shape.isResizable) {
                shape.bounds.set(this.orgPos[index])
              }
            } else {
              shape.bounds.moveTo(this.orgPos[index])
            }
          }.bind(this)
        )

        //this.facade.getCanvas().update();
        //this.facade.updateSelection();
      }
    })

    var command = new commandClass(elements, bounds, maxHeight, maxWidth, parseInt(way), this)

    this.facade.executeCommands([command])
  }
})
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

if (!ORYX.Plugins) ORYX.Plugins = new Object()

ORYX.Plugins.Save = Clazz.extend({
  facade: undefined,

  processURI: undefined,

  changeSymbol: '*',

  construct: function(facade) {
    this.facade = facade

    document.addEventListener(
      'keydown',
      function(e) {
        if (e.ctrlKey && e.keyCode === 83) {
          Event.stop(e)
        }
      },
      false
    )

    window.onbeforeunload = this.onUnLoad.bind(this)

    this.changeDifference = 0

    // Register on event for executing commands --> store all commands in a stack
    // --> Execute
    this.facade.registerOnEvent(
      ORYX.CONFIG.EVENT_UNDO_EXECUTE,
      function() {
        this.changeDifference++
        this.updateTitle()
      }.bind(this)
    )
    this.facade.registerOnEvent(
      ORYX.CONFIG.EVENT_EXECUTE_COMMANDS,
      function() {
        this.changeDifference++
        this.updateTitle()
      }.bind(this)
    )
    // --> Saved from other places in the editor
    this.facade.registerOnEvent(
      ORYX.CONFIG.EVENT_SAVED,
      function() {
        this.changeDifference = 0
        this.updateTitle()
      }.bind(this)
    )

    // --> Rollback
    this.facade.registerOnEvent(
      ORYX.CONFIG.EVENT_UNDO_ROLLBACK,
      function() {
        this.changeDifference--
        this.updateTitle()
      }.bind(this)
    )

    //TODO very critical for load time performance!!!
    //this.serializedDOM = DataManager.__persistDOM(this.facade);

    this.hasChanges = this._hasChanges.bind(this)
  },

  updateTitle: function() {
    var value = window.document.title || document.getElementsByTagName('title')[0].childNodes[0].nodeValue

    if (this.changeDifference === 0 && value.startsWith(this.changeSymbol)) {
      window.document.title = value.slice(1)
    } else if (this.changeDifference !== 0 && !value.startsWith(this.changeSymbol)) {
      window.document.title = this.changeSymbol + '' + value
    }
  },

  _hasChanges: function() {
    return (
      this.changeDifference !== 0 ||
      (this.facade.getModelMetaData()['new'] &&
        this.facade
          .getCanvas()
          .getChildShapes()
          .size() > 0)
    )
  },

  onUnLoad: function() {
    if (this._hasChanges()) {
      return ORYX.I18N.Save.unsavedData
    }
  }
})
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

if (!ORYX.Plugins) ORYX.Plugins = new Object()

ORYX.Plugins.DragDropResize = ORYX.Plugins.AbstractPlugin.extend({
  /**
   *	Constructor
   *	@param {Object} Facade: The Facade of the Editor
   */
  construct: function(facade) {
    this.facade = facade

    // Initialize variables
    this.currentShapes = [] // Current selected Shapes
    //this.pluginsData 		= [];			// Available Plugins
    this.toMoveShapes = [] // Shapes there will be moved
    this.distPoints = [] // Distance Points for Snap on Grid
    this.isResizing = false // Flag: If there was currently resized
    this.dragEnable = false // Flag: If Dragging is enabled
    this.dragIntialized = false // Flag: If the Dragging is initialized
    this.edgesMovable = true // Flag: If an edge is docked it is not movable
    this.offSetPosition = { x: 0, y: 0 } // Offset of the Dragging
    this.faktorXY = { x: 1, y: 1 } // The Current Zoom-Faktor
    this.containmentParentNode // the current future parent node for the dragged shapes
    this.isAddingAllowed = false // flag, if adding current selected shapes to containmentParentNode is allowed
    this.isAttachingAllowed = false // flag, if attaching to the current shape is allowed

    this.callbackMouseMove = this.handleMouseMove.bind(this)
    this.callbackMouseUp = this.handleMouseUp.bind(this)

    // Get the SVG-Containernode
    var containerNode = this.facade.getCanvas().getSvgContainer()

    // Create the Selected Rectangle in the SVG
    this.selectedRect = new ORYX.Plugins.SelectedRect(containerNode)

    // Show grid line if enabled
    if (ORYX.CONFIG.SHOW_GRIDLINE) {
      this.vLine = new ORYX.Plugins.GridLine(containerNode, ORYX.Plugins.GridLine.DIR_VERTICAL)
      this.hLine = new ORYX.Plugins.GridLine(containerNode, ORYX.Plugins.GridLine.DIR_HORIZONTAL)
    }

    // Get a HTML-ContainerNode
    containerNode = this.facade.getCanvas().getHTMLContainer()

    this.scrollNode = this.facade.getCanvas().rootNode.parentNode.parentNode

    // Create the southeastern button for resizing
    this.resizerSE = new ORYX.Plugins.Resizer(containerNode, 'southeast', this.facade)
    this.resizerSE.registerOnResize(this.onResize.bind(this)) // register the resize callback
    this.resizerSE.registerOnResizeEnd(this.onResizeEnd.bind(this)) // register the resize end callback
    this.resizerSE.registerOnResizeStart(this.onResizeStart.bind(this)) // register the resize start callback

    // Create the northwestern button for resizing
    this.resizerNW = new ORYX.Plugins.Resizer(containerNode, 'northwest', this.facade)
    this.resizerNW.registerOnResize(this.onResize.bind(this)) // register the resize callback
    this.resizerNW.registerOnResizeEnd(this.onResizeEnd.bind(this)) // register the resize end callback
    this.resizerNW.registerOnResizeStart(this.onResizeStart.bind(this)) // register the resize start callback

    // For the Drag and Drop
    // Register on MouseDown-Event on a Shape
    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_MOUSEDOWN, this.handleMouseDown.bind(this))
  },

  /**
   * On Mouse Down
   *
   */
  handleMouseDown: function(event, uiObj) {
    // If the selection Bounds not intialized and the uiObj is not member of current selectio
    // then return
    if (!this.dragBounds || !this.currentShapes.member(uiObj) || !this.toMoveShapes.length) {
      return
    }

    // Start Dragging
    this.dragEnable = true
    this.dragIntialized = true
    this.edgesMovable = true

    // Calculate the current zoom factor
    var a = this.facade.getCanvas().node.getScreenCTM()
    this.faktorXY.x = a.a
    this.faktorXY.y = a.d

    var eventX = Event.pointerX(event)
    var eventY = Event.pointerY(event)

    // Set the offset position of dragging
    var upL = this.dragBounds.upperLeft()
    this.offSetPosition = {
      x: eventX - upL.x * this.faktorXY.x,
      y: eventY - upL.y * this.faktorXY.y
    }

    this.offsetScroll = { x: this.scrollNode.scrollLeft, y: this.scrollNode.scrollTop }

    // Register on Global Mouse-MOVE Event
    document.documentElement.addEventListener(ORYX.CONFIG.EVENT_MOUSEMOVE, this.callbackMouseMove, false)
    // Register on Global Mouse-UP Event
    document.documentElement.addEventListener(ORYX.CONFIG.EVENT_MOUSEUP, this.callbackMouseUp, true)

    return
  },

  /**
   * On Key Mouse Up
   *
   */
  handleMouseUp: function(event) {
    //disable containment highlighting
    this.facade.raiseEvent({
      type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
      highlightId: 'dragdropresize.contain'
    })

    this.facade.raiseEvent({
      type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
      highlightId: 'dragdropresize.attached'
    })

    // If Dragging is finished
    if (this.dragEnable) {
      // and update the current selection
      if (!this.dragIntialized) {
        // Do Method after Dragging
        this.afterDrag()

        // Check if the Shape is allowed to dock to the other Shape
        if (
          this.isAttachingAllowed &&
          this.toMoveShapes.length == 1 &&
          this.toMoveShapes[0] instanceof ORYX.Core.Node &&
          this.toMoveShapes[0].dockers.length > 0
        ) {
          // Get the position and the docker
          var position = this.facade.eventCoordinates(event)
          var docker = this.toMoveShapes[0].dockers[0]

          //Command-Pattern for dragging several Shapes
          var dockCommand = ORYX.Core.Command.extend({
            construct: function(docker, position, newDockedShape, facade) {
              this.docker = docker
              this.newPosition = position
              this.newDockedShape = newDockedShape
              this.newParent = newDockedShape.parent || facade.getCanvas()
              this.oldPosition = docker.parent.bounds.center()
              this.oldDockedShape = docker.getDockedShape()
              this.oldParent = docker.parent.parent || facade.getCanvas()
              this.facade = facade

              if (this.oldDockedShape) {
                this.oldPosition = docker.parent.absoluteBounds().center()
              }
            },
            execute: function() {
              this.dock(this.newDockedShape, this.newParent, this.newPosition)

              // Raise Event for having the docked shape on top of the other shape
              this.facade.raiseEvent({ type: ORYX.CONFIG.EVENT_ARRANGEMENT_TOP, excludeCommand: true })
            },
            rollback: function() {
              this.dock(this.oldDockedShape, this.oldParent, this.oldPosition)
            },
            dock: function(toDockShape, parent, pos) {
              // Add to the same parent Shape
              parent.add(this.docker.parent)

              // Set the Docker to the new Shape
              this.docker.setDockedShape(undefined)
              this.docker.bounds.centerMoveTo(pos)
              this.docker.setDockedShape(toDockShape)
              //this.docker.update();

              this.facade.setSelection([this.docker.parent])
              this.facade.getCanvas().update()
              this.facade.updateSelection()
            }
          })

          // Instanziate the dockCommand
          var commands = [new dockCommand(docker, position, this.containmentParentNode, this.facade)]
          this.facade.executeCommands(commands)

          // Check if adding is allowed to the other Shape
        } else if (this.isAddingAllowed) {
          // Refresh all Shapes --> Set the new Bounds
          this.refreshSelectedShapes()
        }

        this.facade.updateSelection()

        //this.currentShapes.each(function(shape) {shape.update()})
        // Raise Event: Dragging is finished
        this.facade.raiseEvent({ type: ORYX.CONFIG.EVENT_DRAGDROP_END })
      }

      if (this.vLine) this.vLine.hide()
      if (this.hLine) this.hLine.hide()
    }

    // Disable
    this.dragEnable = false

    // UnRegister on Global Mouse-UP/-Move Event
    document.documentElement.removeEventListener(ORYX.CONFIG.EVENT_MOUSEUP, this.callbackMouseUp, true)
    document.documentElement.removeEventListener(ORYX.CONFIG.EVENT_MOUSEMOVE, this.callbackMouseMove, false)

    return
  },

  /**
   * On Key Mouse Move
   *
   */
  handleMouseMove: function(event) {
    // If dragging is not enabled, go return
    if (!this.dragEnable) {
      return
    }
    // If Dragging is initialized
    if (this.dragIntialized) {
      // Raise Event: Drag will be started
      this.facade.raiseEvent({ type: ORYX.CONFIG.EVENT_DRAGDROP_START })
      this.dragIntialized = false

      // And hide the resizers and the highlighting
      this.resizerSE.hide()
      this.resizerNW.hide()

      // if only edges are selected, containmentParentNode must be the canvas
      this._onlyEdges = this.currentShapes.all(function(currentShape) {
        return currentShape instanceof ORYX.Core.Edge
      })

      // Do method before Drag
      this.beforeDrag()

      this._currentUnderlyingNodes = []
    }

    // Calculate the new position
    var position = {
      x: Event.pointerX(event) - this.offSetPosition.x,
      y: Event.pointerY(event) - this.offSetPosition.y
    }

    position.x -= this.offsetScroll.x - this.scrollNode.scrollLeft
    position.y -= this.offsetScroll.y - this.scrollNode.scrollTop

    // If not the Control-Key are pressed
    var modifierKeyPressed = event.shiftKey || event.ctrlKey
    if (ORYX.CONFIG.GRID_ENABLED && !modifierKeyPressed) {
      // Snap the current position to the nearest Snap-Point
      position = this.snapToGrid(position)
    } else {
      if (this.vLine) this.vLine.hide()
      if (this.hLine) this.hLine.hide()
    }

    // Adjust the point by the zoom faktor
    position.x /= this.faktorXY.x
    position.y /= this.faktorXY.y

    // Set that the position is not lower than zero
    position.x = Math.max(0, position.x)
    position.y = Math.max(0, position.y)

    // Set that the position is not bigger than the canvas
    var c = this.facade.getCanvas()
    position.x = Math.min(c.bounds.width() - this.dragBounds.width(), position.x)
    position.y = Math.min(c.bounds.height() - this.dragBounds.height(), position.y)

    // Drag this bounds
    this.dragBounds.moveTo(position)

    // Update all selected shapes and the selection rectangle
    //this.refreshSelectedShapes();
    this.resizeRectangle(this.dragBounds)

    this.isAttachingAllowed = false

    //check, if a node can be added to the underlying node
    var eventCoordinates = this.facade.eventCoordinates(event)

    var additionalIEZoom = 1
    if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) {
      var ua = navigator.userAgent
      if (ua.indexOf('MSIE') >= 0) {
        //IE 10 and below
        var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100)
        if (zoom !== 100) {
          additionalIEZoom = zoom / 100
        }
      }
    }

    if (additionalIEZoom !== 1) {
      eventCoordinates.x = eventCoordinates.x / additionalIEZoom
      eventCoordinates.y = eventCoordinates.y / additionalIEZoom
    }

    var underlyingNodes = $A(this.facade.getCanvas().getAbstractShapesAtPosition(eventCoordinates))

    var checkIfAttachable =
      this.toMoveShapes.length == 1 &&
      this.toMoveShapes[0] instanceof ORYX.Core.Node &&
      this.toMoveShapes[0].dockers.length > 0
    checkIfAttachable = checkIfAttachable && underlyingNodes.length != 1

    if (
      !checkIfAttachable &&
      underlyingNodes.length === this._currentUnderlyingNodes.length &&
      underlyingNodes.all(
        function(node, index) {
          return this._currentUnderlyingNodes[index] === node
        }.bind(this)
      )
    ) {
      return
    } else if (this._onlyEdges) {
      this.isAddingAllowed = true
      this.containmentParentNode = this.facade.getCanvas()
    } else {
      /* Check the containment and connection rules */
      var options = {
        event: event,
        underlyingNodes: underlyingNodes,
        checkIfAttachable: checkIfAttachable
      }
      this.checkRules(options)
    }

    this._currentUnderlyingNodes = underlyingNodes.reverse()

    //visualize the containment result
    if (this.isAttachingAllowed) {
      this.facade.raiseEvent({
        type: ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW,
        highlightId: 'dragdropresize.attached',
        elements: [this.containmentParentNode],
        style: ORYX.CONFIG.SELECTION_HIGHLIGHT_STYLE_RECTANGLE,
        color: ORYX.CONFIG.SELECTION_VALID_COLOR
      })
    } else {
      this.facade.raiseEvent({
        type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
        highlightId: 'dragdropresize.attached'
      })
    }

    if (!this.isAttachingAllowed) {
      if (this.isAddingAllowed) {
        this.facade.raiseEvent({
          type: ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW,
          highlightId: 'dragdropresize.contain',
          elements: [this.containmentParentNode],
          color: ORYX.CONFIG.SELECTION_VALID_COLOR
        })
      } else {
        this.facade.raiseEvent({
          type: ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW,
          highlightId: 'dragdropresize.contain',
          elements: [this.containmentParentNode],
          color: ORYX.CONFIG.SELECTION_INVALID_COLOR
        })
      }
    } else {
      this.facade.raiseEvent({
        type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
        highlightId: 'dragdropresize.contain'
      })
    }

    // Stop the Event
    //Event.stop(event);
    return
  },

  //	/**
  //	 * Rollbacks the docked shape of an edge, if the edge is not movable.
  //	 */
  //	redockEdges: function() {
  //		this._undockedEdgesCommand.dockers.each(function(el){
  //			el.docker.setDockedShape(el.dockedShape);
  //			el.docker.setReferencePoint(el.refPoint);
  //		})
  //	},

  /**
   *  Checks the containment and connection rules for the selected shapes.
   */
  checkRules: function(options) {
    var event = options.event
    var underlyingNodes = options.underlyingNodes
    var checkIfAttachable = options.checkIfAttachable
    var noEdges = options.noEdges

    //get underlying node that is not the same than one of the currently selected shapes or
    // a child of one of the selected shapes with the highest z Order.
    // The result is a shape or the canvas
    this.containmentParentNode = underlyingNodes.reverse().find(
      function(node) {
        return (
          node instanceof ORYX.Core.Canvas ||
          ((node instanceof ORYX.Core.Node || (node instanceof ORYX.Core.Edge && !noEdges)) &&
            !(
              this.currentShapes.member(node) ||
              this.currentShapes.any(function(shape) {
                return shape.children.length > 0 && shape.getChildNodes(true).member(node)
              })
            ))
        )
      }.bind(this)
    )

    if (checkIfAttachable) {
      this.isAttachingAllowed = this.facade.getRules().canConnect({
        sourceShape: this.containmentParentNode,
        edgeShape: this.toMoveShapes[0],
        targetShape: this.toMoveShapes[0]
      })

      if (this.isAttachingAllowed) {
        var point = this.facade.eventCoordinates(event)
        this.isAttachingAllowed = this.containmentParentNode.isPointOverOffset(point.x, point.y)
      }
    }

    if (!this.isAttachingAllowed) {
      //check all selected shapes, if they can be added to containmentParentNode
      this.isAddingAllowed = this.toMoveShapes.all(
        function(currentShape) {
          if (
            currentShape instanceof ORYX.Core.Edge ||
            currentShape instanceof ORYX.Core.Controls.Docker ||
            this.containmentParentNode === currentShape.parent
          ) {
            return true
          } else if (this.containmentParentNode !== currentShape) {
            if (!(this.containmentParentNode instanceof ORYX.Core.Edge) || !noEdges) {
              if (
                this.facade
                  .getRules()
                  .canContain({ containingShape: this.containmentParentNode, containedShape: currentShape })
              ) {
                return true
              }
            }
          }
          return false
        }.bind(this)
      )
    }

    if (!this.isAttachingAllowed && !this.isAddingAllowed && this.containmentParentNode instanceof ORYX.Core.Edge) {
      options.noEdges = true
      options.underlyingNodes.reverse()
      this.checkRules(options)
    }
  },

  /**
   * Redraw the selected Shapes.
   *
   */
  refreshSelectedShapes: function() {
    // If the selection bounds not initialized, return
    if (!this.dragBounds) {
      return
    }

    // Calculate the offset between the bounds and the old bounds
    var upL = this.dragBounds.upperLeft()
    var oldUpL = this.oldDragBounds.upperLeft()
    var offset = {
      x: upL.x - oldUpL.x,
      y: upL.y - oldUpL.y
    }

    // Instanciate the dragCommand
    var commands = [
      new ORYX.Core.Command.Move(this.toMoveShapes, offset, this.containmentParentNode, this.currentShapes, this)
    ]
    // If the undocked edges command is setted, add this command
    if (this._undockedEdgesCommand instanceof ORYX.Core.Command) {
      commands.unshift(this._undockedEdgesCommand)
    }
    // Execute the commands
    this.facade.executeCommands(commands)

    // copy the bounds to the old bounds
    if (this.dragBounds) this.oldDragBounds = this.dragBounds.clone()
  },

  /**
   * Callback for Resize
   *
   */
  onResize: function(bounds) {
    // If the selection bounds not initialized, return
    if (!this.dragBounds) {
      return
    }

    this.dragBounds = bounds
    this.isResizing = true

    // Update the rectangle
    this.resizeRectangle(this.dragBounds)
  },

  onResizeStart: function() {
    this.facade.raiseEvent({ type: ORYX.CONFIG.EVENT_RESIZE_START })
  },

  onResizeEnd: function() {
    if (!(this.currentShapes instanceof Array) || this.currentShapes.length <= 0) {
      return
    }

    // If Resizing finished, the Shapes will be resize
    if (this.isResizing) {
      var commandClass = ORYX.Core.Command.extend({
        construct: function(shape, newBounds, plugin) {
          this.shape = shape
          this.oldBounds = shape.bounds.clone()
          this.newBounds = newBounds
          this.plugin = plugin
        },
        execute: function() {
          this.shape.bounds.set(this.newBounds.a, this.newBounds.b)
          this.update(this.getOffset(this.oldBounds, this.newBounds))
        },
        rollback: function() {
          this.shape.bounds.set(this.oldBounds.a, this.oldBounds.b)
          this.update(this.getOffset(this.newBounds, this.oldBounds))
        },

        getOffset: function(b1, b2) {
          return {
            x: b2.a.x - b1.a.x,
            y: b2.a.y - b1.a.y,
            xs: b2.width() / b1.width(),
            ys: b2.height() / b1.height()
          }
        },
        update: function(offset) {
          this.shape.getLabels().each(function(label) {
            label.changed()
          })

          var allEdges = []
            .concat(this.shape.getIncomingShapes())
            .concat(this.shape.getOutgoingShapes())
            // Remove all edges which are included in the selection from the list
            .findAll(
              function(r) {
                return r instanceof ORYX.Core.Edge
              }.bind(this)
            )

          this.plugin.layoutEdges(this.shape, allEdges, offset)

          this.plugin.facade.setSelection([this.shape])
          this.plugin.facade.getCanvas().update()
          this.plugin.facade.updateSelection()
        }
      })

      var bounds = this.dragBounds.clone()
      var shape = this.currentShapes[0]

      if (shape.parent) {
        var parentPosition = shape.parent.absoluteXY()
        bounds.moveBy(-parentPosition.x, -parentPosition.y)
      }

      var command = new commandClass(shape, bounds, this)

      this.facade.executeCommands([command])

      this.isResizing = false

      this.facade.raiseEvent({ type: ORYX.CONFIG.EVENT_RESIZE_END })
    }
  },

  /**
   * Prepare the Dragging
   *
   */
  beforeDrag: function() {
    var undockEdgeCommand = ORYX.Core.Command.extend({
      construct: function(moveShapes) {
        this.dockers = moveShapes
          .collect(function(shape) {
            return shape instanceof ORYX.Core.Controls.Docker
              ? { docker: shape, dockedShape: shape.getDockedShape(), refPoint: shape.referencePoint }
              : undefined
          })
          .compact()
      },
      execute: function() {
        this.dockers.each(function(el) {
          el.docker.setDockedShape(undefined)
        })
      },
      rollback: function() {
        this.dockers.each(function(el) {
          el.docker.setDockedShape(el.dockedShape)
          el.docker.setReferencePoint(el.refPoint)
          //el.docker.update();
        })
      }
    })

    this._undockedEdgesCommand = new undockEdgeCommand(this.toMoveShapes)
    this._undockedEdgesCommand.execute()
  },

  hideAllLabels: function(shape) {
    // Hide all labels from the shape
    shape.getLabels().each(function(label) {
      label.hide()
    })
    // Hide all labels from docked shapes
    shape.getAllDockedShapes().each(function(dockedShape) {
      var labels = dockedShape.getLabels()
      if (labels.length > 0) {
        labels.each(function(label) {
          label.hide()
        })
      }
    })

    // Do this recursive for all child shapes
    // EXP-NICO use getShapes
    shape.getChildren().each(
      function(value) {
        if (value instanceof ORYX.Core.Shape) this.hideAllLabels(value)
      }.bind(this)
    )
  },

  /**
   * Finished the Dragging
   *
   */
  afterDrag: function() {},

  /**
   * Show all Labels at these shape
   *
   */
  showAllLabels: function(shape) {
    // Show the label of these shape
    //shape.getLabels().each(function(label) {
    for (var i = 0; i < shape.length; i++) {
      var label = shape[i]
      label.show()
    } //);
    // Show all labels at docked shapes
    //shape.getAllDockedShapes().each(function(dockedShape) {
    var allDockedShapes = shape.getAllDockedShapes()
    for (var i = 0; i < allDockedShapes.length; i++) {
      var dockedShape = allDockedShapes[i]
      var labels = dockedShape.getLabels()
      if (labels.length > 0) {
        labels.each(function(label) {
          label.show()
        })
      }
    } //);

    // Do this recursive
    //shape.children.each((function(value) {
    for (var i = 0; i < shape.children.length; i++) {
      var value = shape.children[i]
      if (value instanceof ORYX.Core.Shape) this.showAllLabels(value)
    } //).bind(this));
  },

  /**
   * Intialize Method, if there are new Plugins
   *
   */
  /*registryChanged: function(pluginsData) {
		// Save all new Plugin, sorted by group and index
		this.pluginsData = pluginsData.sortBy( function(value) {
			return (value.group + "" + value.index);
		});
	},*/

  /**
   * On the Selection-Changed
   *
   */
  onSelectionChanged: function(event) {
    var elements = event.elements

    // Reset the drag-variables
    this.dragEnable = false
    this.dragIntialized = false
    this.resizerSE.hide()
    this.resizerNW.hide()

    // If there is no elements
    if (!elements || elements.length == 0) {
      // Hide all things and reset all variables
      this.selectedRect.hide()
      this.currentShapes = []
      this.toMoveShapes = []
      this.dragBounds = undefined
      this.oldDragBounds = undefined
    } else {
      // Set the current Shapes
      this.currentShapes = elements

      // Get all shapes with the highest parent in object hierarchy (canvas is the top most parent)
      var topLevelElements = this.facade.getCanvas().getShapesWithSharedParent(elements)
      this.toMoveShapes = topLevelElements

      this.toMoveShapes = this.toMoveShapes.findAll(function(shape) {
        return (
          shape instanceof ORYX.Core.Node &&
          (shape.dockers.length === 0 || !elements.member(shape.dockers.first().getDockedShape()))
        )
      })

      elements.each(
        function(shape) {
          if (!(shape instanceof ORYX.Core.Edge)) {
            return
          }

          var dks = shape.getDockers()

          var hasF = elements.member(dks.first().getDockedShape())
          var hasL = elements.member(dks.last().getDockedShape())

          //				if(!hasL) {
          //					this.toMoveShapes.push(dks.last());
          //				}
          //				if(!hasF){
          //					this.toMoveShapes.push(dks.first())
          //				}
          /* Enable movement of undocked edges */
          if (!hasF && !hasL) {
            var isUndocked = !dks.first().getDockedShape() && !dks.last().getDockedShape()
            if (isUndocked) {
              this.toMoveShapes = this.toMoveShapes.concat(dks)
            }
          }

          if (shape.dockers.length > 2 && hasF && hasL) {
            this.toMoveShapes = this.toMoveShapes.concat(
              dks.findAll(function(el, index) {
                return index > 0 && index < dks.length - 1
              })
            )
          }
        }.bind(this)
      )

      // Calculate the new area-bounds of the selection
      var newBounds = undefined
      this.toMoveShapes.each(
        function(value) {
          var shape = value
          if (value instanceof ORYX.Core.Controls.Docker) {
            /* Get the Shape */
            shape = value.parent
          }

          if (!newBounds) {
            newBounds = shape.absoluteBounds()
          } else {
            newBounds.include(shape.absoluteBounds())
          }
        }.bind(this)
      )

      if (!newBounds) {
        elements.each(function(value) {
          if (!newBounds) {
            newBounds = value.absoluteBounds()
          } else {
            newBounds.include(value.absoluteBounds())
          }
        })
      }

      // Set the new bounds
      this.dragBounds = newBounds
      this.oldDragBounds = newBounds.clone()

      // Update and show the rectangle
      this.resizeRectangle(newBounds)
      this.selectedRect.show()

      // Show the resize button, if there is only one element and this is resizeable
      if (elements.length == 1 && elements[0].isResizable) {
        var aspectRatio = elements[0].getStencil().fixedAspectRatio()
          ? elements[0].bounds.width() / elements[0].bounds.height()
          : undefined
        this.resizerSE.setBounds(this.dragBounds, elements[0].minimumSize, elements[0].maximumSize, aspectRatio)
        this.resizerSE.show()
        this.resizerNW.setBounds(this.dragBounds, elements[0].minimumSize, elements[0].maximumSize, aspectRatio)
        this.resizerNW.show()
      } else {
        this.resizerSE.setBounds(undefined)
        this.resizerNW.setBounds(undefined)
      }

      // If Snap-To-Grid is enabled, the Snap-Point will be calculate
      if (ORYX.CONFIG.GRID_ENABLED) {
        // Reset all points
        this.distPoints = []

        if (this.distPointTimeout) window.clearTimeout(this.distPointTimeout)

        this.distPointTimeout = window.setTimeout(
          function() {
            // Get all the shapes, there will consider at snapping
            // Consider only those elements who shares the same parent element
            var distShapes = this.facade
              .getCanvas()
              .getChildShapes(true)
              .findAll(function(value) {
                var parentShape = value.parent
                while (parentShape) {
                  if (elements.member(parentShape)) return false
                  parentShape = parentShape.parent
                }
                return true
              })

            // The current selection will delete from this array
            //elements.each(function(shape) {
            //	distShapes = distShapes.without(shape);
            //});

            // For all these shapes
            distShapes.each(
              function(value) {
                if (!(value instanceof ORYX.Core.Edge)) {
                  var ul = value.absoluteXY()
                  var width = value.bounds.width()
                  var height = value.bounds.height()

                  // Add the upperLeft, center and lowerRight - Point to the distancePoints
                  this.distPoints.push({
                    ul: {
                      x: ul.x,
                      y: ul.y
                    },
                    c: {
                      x: ul.x + width / 2,
                      y: ul.y + height / 2
                    },
                    lr: {
                      x: ul.x + width,
                      y: ul.y + height
                    }
                  })
                }
              }.bind(this)
            )
          }.bind(this),
          10
        )
      }
    }
  },

  /**
   * Adjust an Point to the Snap Points
   *
   */
  snapToGrid: function(position) {
    // Get the current Bounds
    var bounds = this.dragBounds

    var point = {}

    var ulThres = 6
    var cThres = 10
    var lrThres = 6

    var scale = this.vLine ? this.vLine.getScale() : 1

    var ul = { x: position.x / scale, y: position.y / scale }
    var c = { x: position.x / scale + bounds.width() / 2, y: position.y / scale + bounds.height() / 2 }
    var lr = { x: position.x / scale + bounds.width(), y: position.y / scale + bounds.height() }

    var offsetX, offsetY
    var gridX, gridY

    // For each distant point
    this.distPoints.each(function(value) {
      var x, y, gx, gy
      if (Math.abs(value.c.x - c.x) < cThres) {
        x = value.c.x - c.x
        gx = value.c.x
      } /* else if (Math.abs(value.ul.x-ul.x) < ulThres){
				x = value.ul.x-ul.x;
				gx = value.ul.x;
			} else if (Math.abs(value.lr.x-lr.x) < lrThres){
				x = value.lr.x-lr.x;
				gx = value.lr.x;
			} */

      if (Math.abs(value.c.y - c.y) < cThres) {
        y = value.c.y - c.y
        gy = value.c.y
      } /* else if (Math.abs(value.ul.y-ul.y) < ulThres){
				y = value.ul.y-ul.y;
				gy = value.ul.y;
			} else if (Math.abs(value.lr.y-lr.y) < lrThres){
				y = value.lr.y-lr.y;
				gy = value.lr.y;
			} */

      if (x !== undefined) {
        offsetX = offsetX === undefined ? x : Math.abs(x) < Math.abs(offsetX) ? x : offsetX
        if (offsetX === x) gridX = gx
      }

      if (y !== undefined) {
        offsetY = offsetY === undefined ? y : Math.abs(y) < Math.abs(offsetY) ? y : offsetY
        if (offsetY === y) gridY = gy
      }
    })

    if (offsetX !== undefined) {
      ul.x += offsetX
      ul.x *= scale
      if (this.vLine && gridX) this.vLine.update(gridX)
    } else {
      ul.x = position.x - (position.x % (ORYX.CONFIG.GRID_DISTANCE / 2))
      if (this.vLine) this.vLine.hide()
    }

    if (offsetY !== undefined) {
      ul.y += offsetY
      ul.y *= scale
      if (this.hLine && gridY) this.hLine.update(gridY)
    } else {
      ul.y = position.y - (position.y % (ORYX.CONFIG.GRID_DISTANCE / 2))
      if (this.hLine) this.hLine.hide()
    }

    return ul
  },

  showGridLine: function() {},

  /**
   * Redraw of the Rectangle of the SelectedArea
   * @param {Object} bounds
   */
  resizeRectangle: function(bounds) {
    // Resize the Rectangle
    this.selectedRect.resize(bounds)
  }
})

ORYX.Plugins.SelectedRect = Clazz.extend({
  construct: function(parentId) {
    this.parentId = parentId

    this.node = ORYX.Editor.graft('http://www.w3.org/2000/svg', $(parentId), ['g'])

    this.dashedArea = ORYX.Editor.graft('http://www.w3.org/2000/svg', this.node, [
      'rect',
      {
        x: 0,
        y: 0,
        'stroke-width': 1,
        stroke: '#777777',
        fill: 'none',
        'stroke-dasharray': '2,2',
        'pointer-events': 'none'
      }
    ])

    this.hide()
  },

  hide: function() {
    this.node.setAttributeNS(null, 'display', 'none')
  },

  show: function() {
    this.node.setAttributeNS(null, 'display', '')
  },

  resize: function(bounds) {
    var upL = bounds.upperLeft()

    var padding = ORYX.CONFIG.SELECTED_AREA_PADDING

    this.dashedArea.setAttributeNS(null, 'width', bounds.width() + 2 * padding)
    this.dashedArea.setAttributeNS(null, 'height', bounds.height() + 2 * padding)
    this.node.setAttributeNS(null, 'transform', 'translate(' + (upL.x - padding) + ', ' + (upL.y - padding) + ')')
  }
})

ORYX.Plugins.GridLine = Clazz.extend({
  construct: function(parentId, direction) {
    if (ORYX.Plugins.GridLine.DIR_HORIZONTAL !== direction && ORYX.Plugins.GridLine.DIR_VERTICAL !== direction) {
      direction = ORYX.Plugins.GridLine.DIR_HORIZONTAL
    }

    this.parent = $(parentId)
    this.direction = direction
    this.node = ORYX.Editor.graft('http://www.w3.org/2000/svg', this.parent, ['g'])

    this.line = ORYX.Editor.graft('http://www.w3.org/2000/svg', this.node, [
      'path',
      {
        'stroke-width': 1,
        stroke: 'silver',
        fill: 'none',
        'stroke-dasharray': '5,5',
        'pointer-events': 'none'
      }
    ])

    this.hide()
  },

  hide: function() {
    this.node.setAttributeNS(null, 'display', 'none')
  },

  show: function() {
    this.node.setAttributeNS(null, 'display', '')
  },

  getScale: function() {
    try {
      return this.parent.parentNode.transform.baseVal.getItem(0).matrix.a
    } catch (e) {
      return 1
    }
  },

  update: function(pos) {
    if (this.direction === ORYX.Plugins.GridLine.DIR_HORIZONTAL) {
      var y = pos instanceof Object ? pos.y : pos
      var cWidth = this.parent.parentNode.parentNode.width.baseVal.value / this.getScale()
      this.line.setAttributeNS(null, 'd', 'M 0 ' + y + ' L ' + cWidth + ' ' + y)
    } else {
      var x = pos instanceof Object ? pos.x : pos
      var cHeight = this.parent.parentNode.parentNode.height.baseVal.value / this.getScale()
      this.line.setAttributeNS(null, 'd', 'M' + x + ' 0 L ' + x + ' ' + cHeight)
    }

    this.show()
  }
})

ORYX.Plugins.GridLine.DIR_HORIZONTAL = 'hor'
ORYX.Plugins.GridLine.DIR_VERTICAL = 'ver'

ORYX.Plugins.Resizer = Clazz.extend({
  construct: function(parentId, orientation, facade) {
    this.parentId = parentId
    this.orientation = orientation
    this.facade = facade

    this.node = ORYX.Editor.graft('http://www.w3.org/1999/xhtml', $('canvasSection'), [
      'div',
      { class: 'resizer_' + this.orientation, style: 'left:0px; top:0px;position:absolute;' }
    ])

    this.node.addEventListener(ORYX.CONFIG.EVENT_MOUSEDOWN, this.handleMouseDown.bind(this), true)
    document.documentElement.addEventListener(ORYX.CONFIG.EVENT_MOUSEUP, this.handleMouseUp.bind(this), true)
    document.documentElement.addEventListener(ORYX.CONFIG.EVENT_MOUSEMOVE, this.handleMouseMove.bind(this), false)

    this.dragEnable = false
    this.offSetPosition = { x: 0, y: 0 }
    this.bounds = undefined

    this.canvasNode = this.facade.getCanvas().node

    this.minSize = undefined
    this.maxSize = undefined

    this.aspectRatio = undefined

    this.resizeCallbacks = []
    this.resizeStartCallbacks = []
    this.resizeEndCallbacks = []
    this.hide()

    // Calculate the Offset
    this.scrollNode = this.node.parentNode.parentNode.parentNode
  },

  handleMouseDown: function(event) {
    this.dragEnable = true

    this.offsetScroll = { x: this.scrollNode.scrollLeft, y: this.scrollNode.scrollTop }

    this.offSetPosition = {
      x: Event.pointerX(event) - this.position.x,
      y: Event.pointerY(event) - this.position.y
    }

    this.resizeStartCallbacks.each(
      function(value) {
        value(this.bounds)
      }.bind(this)
    )
  },

  handleMouseUp: function(event) {
    this.dragEnable = false
    this.containmentParentNode = null
    this.resizeEndCallbacks.each(
      function(value) {
        value(this.bounds)
      }.bind(this)
    )
  },

  handleMouseMove: function(event) {
    if (!this.dragEnable) {
      return
    }

    if (event.shiftKey || event.ctrlKey) {
      this.aspectRatio = this.bounds.width() / this.bounds.height()
    } else {
      this.aspectRatio = undefined
    }

    var position = {
      x: Event.pointerX(event) - this.offSetPosition.x,
      y: Event.pointerY(event) - this.offSetPosition.y
    }

    position.x -= this.offsetScroll.x - this.scrollNode.scrollLeft
    position.y -= this.offsetScroll.y - this.scrollNode.scrollTop

    position.x = Math.min(position.x, this.facade.getCanvas().bounds.width())
    position.y = Math.min(position.y, this.facade.getCanvas().bounds.height())

    var offset = {
      x: position.x - this.position.x,
      y: position.y - this.position.y
    }

    if (this.aspectRatio) {
      // fixed aspect ratio
      newAspectRatio = (this.bounds.width() + offset.x) / (this.bounds.height() + offset.y)
      if (newAspectRatio > this.aspectRatio) {
        offset.x = this.aspectRatio * (this.bounds.height() + offset.y) - this.bounds.width()
      } else if (newAspectRatio < this.aspectRatio) {
        offset.y = (this.bounds.width() + offset.x) / this.aspectRatio - this.bounds.height()
      }
    }

    // respect minimum and maximum sizes of stencil
    if (this.orientation === 'northwest') {
      if (this.bounds.width() - offset.x > this.maxSize.width) {
        offset.x = -(this.maxSize.width - this.bounds.width())
        if (this.aspectRatio) offset.y = this.aspectRatio * offset.x
      }
      if (this.bounds.width() - offset.x < this.minSize.width) {
        offset.x = -(this.minSize.width - this.bounds.width())
        if (this.aspectRatio) offset.y = this.aspectRatio * offset.x
      }
      if (this.bounds.height() - offset.y > this.maxSize.height) {
        offset.y = -(this.maxSize.height - this.bounds.height())
        if (this.aspectRatio) offset.x = offset.y / this.aspectRatio
      }
      if (this.bounds.height() - offset.y < this.minSize.height) {
        offset.y = -(this.minSize.height - this.bounds.height())
        if (this.aspectRatio) offset.x = offset.y / this.aspectRatio
      }
    } else {
      // defaults to southeast
      if (this.bounds.width() + offset.x > this.maxSize.width) {
        offset.x = this.maxSize.width - this.bounds.width()
        if (this.aspectRatio) offset.y = this.aspectRatio * offset.x
      }
      if (this.bounds.width() + offset.x < this.minSize.width) {
        offset.x = this.minSize.width - this.bounds.width()
        if (this.aspectRatio) offset.y = this.aspectRatio * offset.x
      }
      if (this.bounds.height() + offset.y > this.maxSize.height) {
        offset.y = this.maxSize.height - this.bounds.height()
        if (this.aspectRatio) offset.x = offset.y / this.aspectRatio
      }
      if (this.bounds.height() + offset.y < this.minSize.height) {
        offset.y = this.minSize.height - this.bounds.height()
        if (this.aspectRatio) offset.x = offset.y / this.aspectRatio
      }
    }

    if (this.orientation === 'northwest') {
      this.bounds.extend({ x: -offset.x, y: -offset.y })
      this.bounds.moveBy(offset)
    } else {
      // defaults to southeast
      this.bounds.extend(offset)
    }

    this.update()

    this.resizeCallbacks.each(
      function(value) {
        value(this.bounds)
      }.bind(this)
    )

    Event.stop(event)
  },

  registerOnResizeStart: function(callback) {
    if (!this.resizeStartCallbacks.member(callback)) {
      this.resizeStartCallbacks.push(callback)
    }
  },

  unregisterOnResizeStart: function(callback) {
    if (this.resizeStartCallbacks.member(callback)) {
      this.resizeStartCallbacks = this.resizeStartCallbacks.without(callback)
    }
  },

  registerOnResizeEnd: function(callback) {
    if (!this.resizeEndCallbacks.member(callback)) {
      this.resizeEndCallbacks.push(callback)
    }
  },

  unregisterOnResizeEnd: function(callback) {
    if (this.resizeEndCallbacks.member(callback)) {
      this.resizeEndCallbacks = this.resizeEndCallbacks.without(callback)
    }
  },

  registerOnResize: function(callback) {
    if (!this.resizeCallbacks.member(callback)) {
      this.resizeCallbacks.push(callback)
    }
  },

  unregisterOnResize: function(callback) {
    if (this.resizeCallbacks.member(callback)) {
      this.resizeCallbacks = this.resizeCallbacks.without(callback)
    }
  },

  hide: function() {
    this.node.style.display = 'none'
  },

  show: function() {
    if (this.bounds) this.node.style.display = ''
  },

  setBounds: function(bounds, min, max, aspectRatio) {
    this.bounds = bounds

    if (!min) min = { width: ORYX.CONFIG.MINIMUM_SIZE, height: ORYX.CONFIG.MINIMUM_SIZE }

    if (!max) max = { width: ORYX.CONFIG.MAXIMUM_SIZE, height: ORYX.CONFIG.MAXIMUM_SIZE }

    this.minSize = min
    this.maxSize = max

    this.aspectRatio = aspectRatio

    this.update()
  },

  update: function() {
    if (!this.bounds) {
      return
    }

    var upL = this.bounds.upperLeft()

    if (this.bounds.width() < this.minSize.width) {
      this.bounds.set(upL.x, upL.y, upL.x + this.minSize.width, upL.y + this.bounds.height())
    }
    if (this.bounds.height() < this.minSize.height) {
      this.bounds.set(upL.x, upL.y, upL.x + this.bounds.width(), upL.y + this.minSize.height)
    }
    if (this.bounds.width() > this.maxSize.width) {
      this.bounds.set(upL.x, upL.y, upL.x + this.maxSize.width, upL.y + this.bounds.height())
    }
    if (this.bounds.height() > this.maxSize.height) {
      this.bounds.set(upL.x, upL.y, upL.x + this.bounds.width(), upL.y + this.maxSize.height)
    }

    var a = this.canvasNode.getScreenCTM()

    upL.x *= a.a
    upL.y *= a.d

    var additionalIEZoom = 1
    if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) {
      var ua = navigator.userAgent
      if (ua.indexOf('MSIE') >= 0) {
        //IE 10 and below
        var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100)
        if (zoom !== 100) {
          additionalIEZoom = zoom / 100
        }
      }
    }

    if (additionalIEZoom === 1) {
      upL.y = upL.y - jQuery('#canvasSection').offset().top + a.f
      upL.x = upL.x - jQuery('#canvasSection').offset().left + a.e
    } else {
      var canvasOffsetLeft = jQuery('#canvasSection').offset().left
      var canvasScrollLeft = jQuery('#canvasSection').scrollLeft()
      var canvasScrollTop = jQuery('#canvasSection').scrollTop()

      var offset = a.e - canvasOffsetLeft * additionalIEZoom
      var additionaloffset = 0
      if (offset > 10) {
        additionaloffset = offset / additionalIEZoom - offset
      }
      upL.y =
        upL.y -
        jQuery('#canvasSection').offset().top * additionalIEZoom +
        (canvasScrollTop * additionalIEZoom - canvasScrollTop) +
        a.f
      upL.x =
        upL.x -
        canvasOffsetLeft * additionalIEZoom +
        additionaloffset +
        (canvasScrollLeft * additionalIEZoom - canvasScrollLeft) +
        a.e
    }

    if (this.orientation === 'northwest') {
      upL.x -= 13
      upL.y -= 13
    } else {
      // defaults to southeast
      upL.x += a.a * this.bounds.width() + 3
      upL.y += a.d * this.bounds.height() + 3
    }

    this.position = upL

    this.node.style.left = this.position.x + 'px'
    this.node.style.top = this.position.y + 'px'
  }
})

/**
 * Implements a Command to move shapes
 *
 */

ORYX.Core.Command.Move = ORYX.Core.Command.extend({
  construct: function(moveShapes, offset, parent, selectedShapes, plugin) {
    this.moveShapes = moveShapes
    this.selectedShapes = selectedShapes
    this.offset = offset
    this.plugin = plugin
    // Defines the old/new parents for the particular shape
    this.newParents = moveShapes.collect(function(t) {
      return parent || t.parent
    })
    this.oldParents = moveShapes.collect(function(shape) {
      return shape.parent
    })
    this.dockedNodes = moveShapes
      .findAll(function(shape) {
        return shape instanceof ORYX.Core.Node && shape.dockers.length == 1
      })
      .collect(function(shape) {
        return {
          docker: shape.dockers[0],
          dockedShape: shape.dockers[0].getDockedShape(),
          refPoint: shape.dockers[0].referencePoint
        }
      })
  },
  execute: function() {
    this.dockAllShapes()
    // Moves by the offset
    this.move(this.offset)
    // Addes to the new parents
    this.addShapeToParent(this.newParents)
    // Set the selection to the current selection
    this.selectCurrentShapes()
    this.plugin.facade.getCanvas().update()
    this.plugin.facade.updateSelection()
  },
  rollback: function() {
    // Moves by the inverted offset
    var offset = { x: -this.offset.x, y: -this.offset.y }
    this.move(offset)
    // Addes to the old parents
    this.addShapeToParent(this.oldParents)
    this.dockAllShapes(true)

    // Set the selection to the current selection
    this.selectCurrentShapes()
    this.plugin.facade.getCanvas().update()
    this.plugin.facade.updateSelection()
  },
  move: function(offset, doLayout) {
    // Move all Shapes by these offset
    for (var i = 0; i < this.moveShapes.length; i++) {
      var value = this.moveShapes[i]
      value.bounds.moveBy(offset)

      if (value instanceof ORYX.Core.Node) {
        ;(value.dockers || []).each(function(d) {
          d.bounds.moveBy(offset)
        })

        // Update all Dockers of Child shapes
        /*var childShapesNodes = value.getChildShapes(true).findAll(function(shape){ return shape instanceof ORYX.Core.Node });							
				var childDockedShapes = childShapesNodes.collect(function(shape){ return shape.getAllDockedShapes() }).flatten().uniq();							
				var childDockedEdge = childDockedShapes.findAll(function(shape){ return shape instanceof ORYX.Core.Edge });							
				childDockedEdge = childDockedEdge.findAll(function(shape){ return shape.getAllDockedShapes().all(function(dsh){ return childShapesNodes.include(dsh) }) });							
				var childDockedDockers = childDockedEdge.collect(function(shape){ return shape.dockers }).flatten();
				
				for (var j = 0; j < childDockedDockers.length; j++) {
					var docker = childDockedDockers[j];
					if (!docker.getDockedShape() && !this.moveShapes.include(docker)) {
						//docker.bounds.moveBy(offset);
						//docker.update();
					}
				}*/

        var allEdges = []
          .concat(value.getIncomingShapes())
          .concat(value.getOutgoingShapes())
          // Remove all edges which are included in the selection from the list
          .findAll(
            function(r) {
              return (
                r instanceof ORYX.Core.Edge &&
                !this.moveShapes.any(function(d) {
                  return d == r || (d instanceof ORYX.Core.Controls.Docker && d.parent == r)
                })
              )
            }.bind(this)
          )
          // Remove all edges which are between the node and a node contained in the selection from the list
          .findAll(
            function(r) {
              return (
                (r.dockers.first().getDockedShape() == value ||
                  !this.moveShapes.include(r.dockers.first().getDockedShape())) &&
                (r.dockers.last().getDockedShape() == value ||
                  !this.moveShapes.include(r.dockers.last().getDockedShape()))
              )
            }.bind(this)
          )

        // Layout all outgoing/incoming edges
        this.plugin.layoutEdges(value, allEdges, offset)

        var allSameEdges = []
          .concat(value.getIncomingShapes())
          .concat(value.getOutgoingShapes())
          // Remove all edges which are included in the selection from the list
          .findAll(
            function(r) {
              return (
                r instanceof ORYX.Core.Edge &&
                r.dockers.first().isDocked() &&
                r.dockers.last().isDocked() &&
                !this.moveShapes.include(r) &&
                !this.moveShapes.any(function(d) {
                  return d == r || (d instanceof ORYX.Core.Controls.Docker && d.parent == r)
                })
              )
            }.bind(this)
          )
          // Remove all edges which are included in the selection from the list
          .findAll(
            function(r) {
              return (
                this.moveShapes.indexOf(r.dockers.first().getDockedShape()) > i ||
                this.moveShapes.indexOf(r.dockers.last().getDockedShape()) > i
              )
            }.bind(this)
          )

        for (var j = 0; j < allSameEdges.length; j++) {
          for (var k = 1; k < allSameEdges[j].dockers.length - 1; k++) {
            var docker = allSameEdges[j].dockers[k]
            if (!docker.getDockedShape() && !this.moveShapes.include(docker)) {
              docker.bounds.moveBy(offset)
            }
          }
        }

        /*var i=-1;
				var nodes = value.getChildShapes(true);
				var allEdges = [];
				while(++i<nodes.length){
					var edges = [].concat(nodes[i].getIncomingShapes())
						.concat(nodes[i].getOutgoingShapes())
						// Remove all edges which are included in the selection from the list
						.findAll(function(r){ return r instanceof ORYX.Core.Edge && !allEdges.include(r) && r.dockers.any(function(d){ return !value.bounds.isIncluded(d.bounds.center)})})
					allEdges = allEdges.concat(edges);
					if (edges.length <= 0){ continue }
					//this.plugin.layoutEdges(nodes[i], edges, offset);
				}*/
      }
    }
  },
  dockAllShapes: function(shouldDocked) {
    // Undock all Nodes
    for (var i = 0; i < this.dockedNodes.length; i++) {
      var docker = this.dockedNodes[i].docker

      docker.setDockedShape(shouldDocked ? this.dockedNodes[i].dockedShape : undefined)
      if (docker.getDockedShape()) {
        docker.setReferencePoint(this.dockedNodes[i].refPoint)
        //docker.update();
      }
    }
  },

  addShapeToParent: function(parents) {
    // For every Shape, add this and reset the position
    for (var i = 0; i < this.moveShapes.length; i++) {
      var currentShape = this.moveShapes[i]
      if (currentShape instanceof ORYX.Core.Node && currentShape.parent !== parents[i]) {
        // Calc the new position
        var unul = parents[i].absoluteXY()
        var csul = currentShape.absoluteXY()
        var x = csul.x - unul.x
        var y = csul.y - unul.y

        // Add the shape to the new contained shape
        parents[i].add(currentShape)
        // Add all attached shapes as well
        currentShape.getOutgoingShapes(
          function(shape) {
            if (shape instanceof ORYX.Core.Node && !this.moveShapes.member(shape)) {
              parents[i].add(shape)
            }
          }.bind(this)
        )

        // Set the new position
        if (currentShape instanceof ORYX.Core.Node && currentShape.dockers.length == 1) {
          var b = currentShape.bounds
          x += b.width() / 2
          y += b.height() / 2
          currentShape.dockers.first().bounds.centerMoveTo(x, y)
        } else {
          currentShape.bounds.moveTo(x, y)
        }
      }

      // Update the shape
      //currentShape.update();
    }
  },
  selectCurrentShapes: function() {
    this.plugin.facade.setSelection(this.selectedShapes)
  }
})
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

if (!ORYX.Plugins) ORYX.Plugins = new Object()

ORYX.Plugins.DragDocker = Clazz.extend({
  /**
   *	Constructor
   *	@param {Object} Facade: The Facade of the Editor
   */
  construct: function(facade) {
    this.facade = facade

    // Set the valid and invalid color
    this.VALIDCOLOR = ORYX.CONFIG.SELECTION_VALID_COLOR
    this.INVALIDCOLOR = ORYX.CONFIG.SELECTION_INVALID_COLOR

    // Define Variables
    this.shapeSelection = undefined
    this.docker = undefined
    this.dockerParent = undefined
    this.dockerSource = undefined
    this.dockerTarget = undefined
    this.lastUIObj = undefined
    this.isStartDocker = undefined
    this.isEndDocker = undefined
    this.undockTreshold = 10
    this.initialDockerPosition = undefined
    this.outerDockerNotMoved = undefined
    this.isValid = false

    // For the Drag and Drop
    // Register on MouseDown-Event on a Docker
    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_MOUSEDOWN, this.handleMouseDown.bind(this))
    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_DOCKERDRAG, this.handleDockerDrag.bind(this))

    // Register on over/out to show / hide a docker
    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_MOUSEOVER, this.handleMouseOver.bind(this))
    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_MOUSEOUT, this.handleMouseOut.bind(this))
  },

  /**
   * MouseOut Handler
   *
   */
  handleMouseOut: function(event, uiObj) {
    // If there is a Docker, hide this
    if (!this.docker && uiObj instanceof ORYX.Core.Controls.Docker) {
      uiObj.hide()
    } else if (!this.docker && uiObj instanceof ORYX.Core.Edge) {
      uiObj.dockers.each(function(docker) {
        docker.hide()
      })
    }
  },

  /**
   * MouseOver Handler
   *
   */
  handleMouseOver: function(event, uiObj) {
    // If there is a Docker, show this
    if (!this.docker && uiObj instanceof ORYX.Core.Controls.Docker) {
      uiObj.show()
    } else if (!this.docker && uiObj instanceof ORYX.Core.Edge) {
      uiObj.dockers.each(function(docker) {
        docker.show()
      })
    }
  },
  /**
   * DockerDrag Handler
   * delegates the uiEvent of the drag event to the mouseDown function
   */
  handleDockerDrag: function(event, uiObj) {
    this.handleMouseDown(event.uiEvent, uiObj)
  },

  /**
   * MouseDown Handler
   *
   */

  handleMouseDown: function(event, uiObj) {
    // If there is a Docker
    if (uiObj instanceof ORYX.Core.Controls.Docker && uiObj.isMovable) {
      /* Buffering shape selection and clear selection*/
      this.shapeSelection = this.facade.getSelection()
      this.facade.setSelection()

      this.docker = uiObj
      this.initialDockerPosition = this.docker.bounds.center()
      this.outerDockerNotMoved = false
      this.dockerParent = uiObj.parent

      // Define command arguments
      this._commandArg = {
        docker: uiObj,
        dockedShape: uiObj.getDockedShape(),
        refPoint: uiObj.referencePoint || uiObj.bounds.center()
      }

      // Show the Docker
      this.docker.show()

      // If the Dockers Parent is an Edge,
      //  and the Docker is either the first or last Docker of the Edge
      if (
        uiObj.parent instanceof ORYX.Core.Edge &&
        (uiObj.parent.dockers.first() == uiObj || uiObj.parent.dockers.last() == uiObj)
      ) {
        // Get the Edge Source or Target
        if (uiObj.parent.dockers.first() == uiObj && uiObj.parent.dockers.last().getDockedShape()) {
          this.dockerTarget = uiObj.parent.dockers.last().getDockedShape()
        } else if (uiObj.parent.dockers.last() == uiObj && uiObj.parent.dockers.first().getDockedShape()) {
          this.dockerSource = uiObj.parent.dockers.first().getDockedShape()
        }
      } else {
        // If there parent is not an Edge, undefined the Source and Target
        this.dockerSource = undefined
        this.dockerTarget = undefined
      }

      this.isStartDocker = this.docker.parent.dockers.first() === this.docker
      this.isEndDocker = this.docker.parent.dockers.last() === this.docker

      // add to canvas while dragging
      this.facade.getCanvas().add(this.docker.parent)

      // Hide all Labels from Docker
      this.docker.parent.getLabels().each(function(label) {
        label.hide()
      })

      var eventCoordinates = this.facade.eventCoordinates(event)
      var additionalIEZoom = 1
      if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) {
        var ua = navigator.userAgent
        if (ua.indexOf('MSIE') >= 0) {
          //IE 10 and below
          var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100)
          if (zoom !== 100) {
            additionalIEZoom = zoom / 100
          }
        }
      }

      if (additionalIEZoom !== 1) {
        eventCoordinates.x = eventCoordinates.x / additionalIEZoom
        eventCoordinates.y = eventCoordinates.y / additionalIEZoom
      }

      // Undocked the Docker from current Shape
      if ((!this.isStartDocker && !this.isEndDocker) || !this.docker.isDocked()) {
        this.docker.setDockedShape(undefined)
        // Set the Docker to the center of the mouse pointer
        this.docker.bounds.centerMoveTo(eventCoordinates)
        this.dockerParent._update()
      } else {
        this.outerDockerNotMoved = true
      }

      var option = { movedCallback: this.dockerMoved.bind(this), upCallback: this.dockerMovedFinished.bind(this) }

      this.startEventPos = eventCoordinates

      // Enable the Docker for Drag'n'Drop, give the mouseMove and mouseUp-Callback with
      ORYX.Core.UIEnableDrag(event, uiObj, option)
    }
  },

  /**
   * Docker MouseMove Handler
   *
   */
  dockerMoved: function(event) {
    this.outerDockerNotMoved = false
    var snapToMagnet = undefined

    if (this.docker.parent) {
      if (this.isStartDocker || this.isEndDocker) {
        // Get the EventPosition and all Shapes on these point
        var evPos = this.facade.eventCoordinates(event)

        var additionalIEZoom = 1
        if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) {
          var ua = navigator.userAgent
          if (ua.indexOf('MSIE') >= 0) {
            //IE 10 and below
            var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100)
            if (zoom !== 100) {
              additionalIEZoom = zoom / 100
            }
          }
        }

        if (additionalIEZoom !== 1) {
          evPos.x = evPos.x / additionalIEZoom
          evPos.y = evPos.y / additionalIEZoom
        }

        if (this.docker.isDocked()) {
          /* Only consider start/end dockers if they are moved over a treshold */
          var distanceDockerPointer = ORYX.Core.Math.getDistancePointToPoint(evPos, this.initialDockerPosition)
          if (distanceDockerPointer < this.undockTreshold) {
            this.outerDockerNotMoved = true
            return
          }

          /* Undock the docker */
          this.docker.setDockedShape(undefined)
          // Set the Docker to the center of the mouse pointer
          //this.docker.bounds.centerMoveTo(evPos);
          this.dockerParent._update()
        }

        var shapes = this.facade.getCanvas().getAbstractShapesAtPosition(evPos)

        // Get the top level Shape on these, but not the same as Dockers parent
        var uiObj = shapes.pop()
        if (this.docker.parent === uiObj) {
          uiObj = shapes.pop()
        }

        // If the top level Shape the same as the last Shape, then return
        if (this.lastUIObj == uiObj) {
          //return;
          // If the top level uiObj instance of Shape and this isn't the parent of the docker
        } else if (uiObj instanceof ORYX.Core.Shape) {
          // Ask by the StencilSet if the source, the edge and the target valid connections.
          if (this.docker.parent instanceof ORYX.Core.Edge) {
            var highestParent = this.getHighestParentBeforeCanvas(uiObj)
            /* Ensure that the shape to dock is not a child shape
             * of the same edge.
             */
            if (highestParent instanceof ORYX.Core.Edge && this.docker.parent === highestParent) {
              this.isValid = false
              this.dockerParent._update()
              return
            }
            this.isValid = false
            var curObj = uiObj,
              orgObj = uiObj
            while (!this.isValid && curObj && !(curObj instanceof ORYX.Core.Canvas)) {
              uiObj = curObj
              this.isValid = this.facade.getRules().canConnect({
                sourceShape: this.dockerSource // Is there a docked source
                  ? this.dockerSource // than set this
                  : this.isStartDocker // if not and if the Docker is the start docker
                  ? uiObj // take the last uiObj
                  : undefined, // if not set it to undefined;
                edgeShape: this.docker.parent,
                targetShape: this.dockerTarget // Is there a docked target
                  ? this.dockerTarget // than set this
                  : this.isEndDocker // if not and if the Docker is not the start docker
                  ? uiObj // take the last uiObj
                  : undefined // if not set it to undefined;
              })
              curObj = curObj.parent
            }

            // Reset uiObj if no
            // valid parent is found
            if (!this.isValid) {
              uiObj = orgObj
            }
          } else {
            this.isValid = this.facade.getRules().canConnect({
              sourceShape: uiObj,
              edgeShape: this.docker.parent,
              targetShape: this.docker.parent
            })
          }

          // If there is a lastUIObj, hide the magnets
          if (this.lastUIObj) {
            this.hideMagnets(this.lastUIObj)
          }

          // If there is a valid connection, show the magnets
          if (this.isValid) {
            this.showMagnets(uiObj)
          }

          // Set the Highlight Rectangle by these value
          this.showHighlight(uiObj, this.isValid ? this.VALIDCOLOR : this.INVALIDCOLOR)

          // Buffer the current Shape
          this.lastUIObj = uiObj
        } else {
          // If there is no top level Shape, then hide the highligting of the last Shape
          this.hideHighlight()
          this.lastUIObj ? this.hideMagnets(this.lastUIObj) : null
          this.lastUIObj = undefined
          this.isValid = false
        }

        // Snap to the nearest Magnet
        if (this.lastUIObj && this.isValid && !(event.shiftKey || event.ctrlKey)) {
          snapToMagnet = this.lastUIObj.magnets.find(function(magnet) {
            return magnet.absoluteBounds().isIncluded(evPos)
          })

          if (snapToMagnet) {
            this.docker.bounds.centerMoveTo(snapToMagnet.absoluteCenterXY())
            //this.docker.update()
          }
        }
      }
    }
    // Snap to on the nearest Docker of the same parent
    if (!(event.shiftKey || event.ctrlKey) && !snapToMagnet) {
      var minOffset = ORYX.CONFIG.DOCKER_SNAP_OFFSET
      var nearestX = minOffset + 1
      var nearestY = minOffset + 1

      var dockerCenter = this.docker.bounds.center()

      if (this.docker.parent) {
        this.docker.parent.dockers.each(
          function(docker) {
            if (this.docker == docker) {
              return
            }

            var center = docker.referencePoint ? docker.getAbsoluteReferencePoint() : docker.bounds.center()

            nearestX = Math.abs(nearestX) > Math.abs(center.x - dockerCenter.x) ? center.x - dockerCenter.x : nearestX
            nearestY = Math.abs(nearestY) > Math.abs(center.y - dockerCenter.y) ? center.y - dockerCenter.y : nearestY
          }.bind(this)
        )

        if (Math.abs(nearestX) < minOffset || Math.abs(nearestY) < minOffset) {
          nearestX = Math.abs(nearestX) < minOffset ? nearestX : 0
          nearestY = Math.abs(nearestY) < minOffset ? nearestY : 0

          this.docker.bounds.centerMoveTo(dockerCenter.x + nearestX, dockerCenter.y + nearestY)
          //this.docker.update()
        } else {
          var previous = this.docker.parent.dockers[Math.max(this.docker.parent.dockers.indexOf(this.docker) - 1, 0)]
          var next = this.docker.parent.dockers[
            Math.min(this.docker.parent.dockers.indexOf(this.docker) + 1, this.docker.parent.dockers.length - 1)
          ]

          if (previous && next && previous !== this.docker && next !== this.docker) {
            var cp = previous.bounds.center()
            var cn = next.bounds.center()
            var cd = this.docker.bounds.center()

            // Checks if the point is on the line between previous and next
            if (ORYX.Core.Math.isPointInLine(cd.x, cd.y, cp.x, cp.y, cn.x, cn.y, 10)) {
              // Get the rise
              var raise = (Number(cn.y) - Number(cp.y)) / (Number(cn.x) - Number(cp.x))
              // Calculate the intersection point
              var intersecX =
                (cp.y - cp.x * raise - (cd.y - cd.x * -Math.pow(raise, -1))) / (-Math.pow(raise, -1) - raise)
              var intersecY = cp.y - cp.x * raise + raise * intersecX

              if (isNaN(intersecX) || isNaN(intersecY)) {
                return
              }

              this.docker.bounds.centerMoveTo(intersecX, intersecY)
            }
          }
        }
      }
    }
    //this.facade.getCanvas().update();
    this.dockerParent._update()
  },

  /**
   * Docker MouseUp Handler
   *
   */
  dockerMovedFinished: function(event) {
    /* Reset to buffered shape selection */
    this.facade.setSelection(this.shapeSelection)

    // Hide the border
    this.hideHighlight()

    // Show all Labels from Docker
    this.dockerParent.getLabels().each(function(label) {
      label.show()
      //label.update();
    })

    // If there is a last top level Shape
    if (this.lastUIObj && (this.isStartDocker || this.isEndDocker)) {
      // If there is a valid connection, the set as a docked Shape to them
      if (this.isValid) {
        this.docker.setDockedShape(this.lastUIObj)

        this.facade.raiseEvent({
          type: ORYX.CONFIG.EVENT_DRAGDOCKER_DOCKED,
          docker: this.docker,
          parent: this.docker.parent,
          target: this.lastUIObj
        })
      }

      this.hideMagnets(this.lastUIObj)
    }

    // Hide the Docker
    this.docker.hide()

    if (this.outerDockerNotMoved) {
      // Get the EventPosition and all Shapes on these point
      var evPos = this.facade.eventCoordinates(event)
      var shapes = this.facade.getCanvas().getAbstractShapesAtPosition(evPos)

      /* Remove edges from selection */
      var shapeWithoutEdges = shapes.findAll(function(node) {
        return node instanceof ORYX.Core.Node
      })
      shapes = shapeWithoutEdges.length ? shapeWithoutEdges : shapes
      this.facade.setSelection(shapes)
    } else {
      //Command-Pattern for dragging one docker
      var dragDockerCommand = ORYX.Core.Command.extend({
        construct: function(docker, newPos, oldPos, newDockedShape, oldDockedShape, facade) {
          this.docker = docker
          this.index = docker.parent.dockers.indexOf(docker)
          this.newPosition = newPos
          this.newDockedShape = newDockedShape
          this.oldPosition = oldPos
          this.oldDockedShape = oldDockedShape
          this.facade = facade
          this.index = docker.parent.dockers.indexOf(docker)
          this.shape = docker.parent
        },
        execute: function() {
          if (!this.docker.parent) {
            this.docker = this.shape.dockers[this.index]
          }
          this.dock(this.newDockedShape, this.newPosition)
          this.removedDockers = this.shape.removeUnusedDockers()
          this.facade.updateSelection()
        },
        rollback: function() {
          this.dock(this.oldDockedShape, this.oldPosition)
          ;(this.removedDockers || $H({})).each(
            function(d) {
              this.shape.add(d.value, Number(d.key))
              this.shape._update(true)
            }.bind(this)
          )
          this.facade.updateSelection()
        },
        dock: function(toDockShape, pos) {
          // Set the Docker to the new Shape
          this.docker.setDockedShape(undefined)
          if (toDockShape) {
            this.docker.setDockedShape(toDockShape)
            this.docker.setReferencePoint(pos)
            //this.docker.update();
            //this.docker.parent._update();
          } else {
            this.docker.bounds.centerMoveTo(pos)
          }

          this.facade.getCanvas().update()
        }
      })

      if (this.docker.parent) {
        // Instanziate the dockCommand
        var command = new dragDockerCommand(
          this.docker,
          this.docker.getDockedShape() ? this.docker.referencePoint : this.docker.bounds.center(),
          this._commandArg.refPoint,
          this.docker.getDockedShape(),
          this._commandArg.dockedShape,
          this.facade
        )
        this.facade.executeCommands([command])
      }
    }

    // Update all Shapes
    //this.facade.updateSelection();

    // Undefined all variables
    this.docker = undefined
    this.dockerParent = undefined
    this.dockerSource = undefined
    this.dockerTarget = undefined
    this.lastUIObj = undefined
  },

  /**
   * Hide the highlighting
   */
  hideHighlight: function() {
    this.facade.raiseEvent({ type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE, highlightId: 'validDockedShape' })
  },

  /**
   * Show the highlighting
   *
   */
  showHighlight: function(uiObj, color) {
    this.facade.raiseEvent({
      type: ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW,
      highlightId: 'validDockedShape',
      elements: [uiObj],
      color: color
    })
  },

  showMagnets: function(uiObj) {
    uiObj.magnets.each(function(magnet) {
      magnet.show()
    })
  },

  hideMagnets: function(uiObj) {
    uiObj.magnets.each(function(magnet) {
      magnet.hide()
    })
  },

  getHighestParentBeforeCanvas: function(shape) {
    if (!(shape instanceof ORYX.Core.Shape)) {
      return undefined
    }

    var parent = shape.parent
    while (parent && !(parent.parent instanceof ORYX.Core.Canvas)) {
      parent = parent.parent
    }

    return parent
  }
})

/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

if (!ORYX.Plugins) ORYX.Plugins = new Object()

ORYX.Plugins.AddDocker = Clazz.extend({
  /**
   *	Constructor
   *	@param {Object} Facade: The Facade of the Editor
   */
  construct: function(facade) {
    this.facade = facade
    this.enableAdd = false
    this.enableRemove = false

    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_MOUSEDOWN, this.handleMouseDown.bind(this))
  },

  setEnableAdd: function(enable) {
    this.enableAdd = enable

    if (this.enableAdd) {
      jQuery('#add-bendpoint-button').addClass('pressed')
    } else {
      jQuery('#add-bendpoint-button').removeClass('pressed')
      jQuery('#add-bendpoint-button').blur()
    }
  },
  setEnableRemove: function(enable) {
    this.enableRemove = enable

    if (this.enableRemove) {
      jQuery('#remove-bendpoint-button').addClass('pressed')
    } else {
      jQuery('#remove-bendpoint-button').removeClass('pressed')
      jQuery('#remove-bendpoint-button').blur()
    }
  },

  enabledAdd: function(enable) {
    return this.enableAdd
  },
  enabledRemove: function() {
    return this.enableRemove
  },

  /**
   * MouseDown Handler
   *
   */

  handleMouseDown: function(event, uiObj) {
    if (this.enabledAdd() && uiObj instanceof ORYX.Core.Edge) {
      this.newDockerCommand({
        edge: uiObj,
        position: this.facade.eventCoordinates(event)
      })
      this.setEnableAdd(false)
    } else if (
      this.enabledRemove() &&
      uiObj instanceof ORYX.Core.Controls.Docker &&
      uiObj.parent instanceof ORYX.Core.Edge
    ) {
      this.newDockerCommand({
        edge: uiObj.parent,
        docker: uiObj
      })
      this.setEnableRemove(false)
    }
    document.body.style.cursor = 'default'
  },

  // Options: edge (required), position (required if add), docker (required if delete)
  newDockerCommand: function(options) {
    if (!options.edge) return

    var commandClass = ORYX.Core.Command.extend({
      construct: function(addEnabled, deleteEnabled, edge, docker, pos, facade) {
        this.addEnabled = addEnabled
        this.deleteEnabled = deleteEnabled
        this.edge = edge
        this.docker = docker
        this.pos = pos
        this.facade = facade
      },
      execute: function() {
        if (this.addEnabled) {
          if (!this.docker) {
            this.docker = this.edge.addDocker(this.pos)
            this.index = this.edge.dockers.indexOf(this.docker)
          } else {
            this.edge.add(this.docker, this.index)
          }
        } else if (this.deleteEnabled) {
          this.index = this.edge.dockers.indexOf(this.docker)
          this.pos = this.docker.bounds.center()
          this.edge.removeDocker(this.docker)
        }
        this.edge.getLabels().invoke('show')
        this.facade.getCanvas().update()
        this.facade.updateSelection()
      },
      rollback: function() {
        if (this.addEnabled) {
          if (this.docker instanceof ORYX.Core.Controls.Docker) {
            this.edge.removeDocker(this.docker)
          }
        } else if (this.deleteEnabled) {
          this.edge.add(this.docker, this.index)
        }
        this.edge.getLabels().invoke('show')
        this.facade.getCanvas().update()
        this.facade.updateSelection()
      }
    })

    var command = new commandClass(
      this.enabledAdd(),
      this.enabledRemove(),
      options.edge,
      options.docker,
      options.position,
      this.facade
    )

    this.facade.executeCommands([command])
  }
})

/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

if (!ORYX.Plugins) ORYX.Plugins = new Object()

ORYX.Plugins.SelectionFrame = Clazz.extend({
  construct: function(facade) {
    this.facade = facade

    // Register on MouseEvents
    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_MOUSEDOWN, this.handleMouseDown.bind(this))
    document.documentElement.addEventListener(ORYX.CONFIG.EVENT_MOUSEUP, this.handleMouseUp.bind(this), true)

    // Some initiale variables
    this.position = { x: 0, y: 0 }
    this.size = { width: 0, height: 0 }
    this.offsetPosition = { x: 0, y: 0 }

    // (Un)Register Mouse-Move Event
    this.moveCallback = undefined
    this.offsetScroll = { x: 0, y: 0 }
    // HTML-Node of Selection-Frame
    this.node = ORYX.Editor.graft('http://www.w3.org/1999/xhtml', $('canvasSection'), [
      'div',
      { class: 'Oryx_SelectionFrame' }
    ])

    this.hide()
  },

  handleMouseDown: function(event, uiObj) {
    // If there is the Canvas
    if (uiObj instanceof ORYX.Core.Canvas) {
      // Calculate the Offset
      var scrollNode = uiObj.rootNode.parentNode.parentNode

      var a = this.facade.getCanvas().node.getScreenCTM()
      this.offsetPosition = {
        x: a.e,
        y: a.f
      }

      // Set the new Position
      this.setPos({
        x: Event.pointerX(event) - jQuery('#canvasSection').offset().left,
        y: Event.pointerY(event) - jQuery('#canvasSection').offset().top + 5
      })

      // Reset the size
      this.resize({ width: 0, height: 0 })
      this.moveCallback = this.handleMouseMove.bind(this)

      // Register Mouse-Move Event
      document.documentElement.addEventListener(ORYX.CONFIG.EVENT_MOUSEMOVE, this.moveCallback, false)

      this.offsetScroll = { x: scrollNode.scrollLeft, y: scrollNode.scrollTop }

      // Show the Frame
      this.show()
    }

    Event.stop(event)
  },

  handleMouseUp: function(event) {
    // If there was an MouseMoving
    if (this.moveCallback) {
      // Hide the Frame
      this.hide()

      // Unregister Mouse-Move
      document.documentElement.removeEventListener(ORYX.CONFIG.EVENT_MOUSEMOVE, this.moveCallback, false)

      this.moveCallback = undefined

      var corrSVG = this.facade.getCanvas().node.getScreenCTM()

      // Calculate the positions of the Frame
      var a = {
        x: this.size.width > 0 ? this.position.x : this.position.x + this.size.width,
        y: this.size.height > 0 ? this.position.y : this.position.y + this.size.height
      }

      var b = {
        x: a.x + Math.abs(this.size.width),
        y: a.y + Math.abs(this.size.height)
      }

      var additionalIEZoom = 1
      if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) {
        var ua = navigator.userAgent
        if (ua.indexOf('MSIE') >= 0) {
          //IE 10 and below
          var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100)
          if (zoom !== 100) {
            additionalIEZoom = zoom / 100
          }
        }
      }

      if (additionalIEZoom === 1) {
        a.x = a.x - (corrSVG.e - jQuery('#canvasSection').offset().left)
        a.y = a.y - (corrSVG.f - jQuery('#canvasSection').offset().top)
        b.x = b.x - (corrSVG.e - jQuery('#canvasSection').offset().left)
        b.y = b.y - (corrSVG.f - jQuery('#canvasSection').offset().top)
      } else {
        var canvasOffsetLeft = jQuery('#canvasSection').offset().left
        var canvasScrollLeft = jQuery('#canvasSection').scrollLeft()
        var canvasScrollTop = jQuery('#canvasSection').scrollTop()

        var offset = a.e - canvasOffsetLeft * additionalIEZoom
        var additionaloffset = 0
        if (offset > 10) {
          additionaloffset = offset / additionalIEZoom - offset
        }

        a.x =
          a.x -
          (corrSVG.e -
            canvasOffsetLeft * additionalIEZoom +
            additionaloffset +
            (canvasScrollLeft * additionalIEZoom - canvasScrollLeft))
        a.y =
          a.y -
          (corrSVG.f -
            jQuery('#canvasSection').offset().top * additionalIEZoom +
            (canvasScrollTop * additionalIEZoom - canvasScrollTop))
        b.x =
          b.x -
          (corrSVG.e -
            canvasOffsetLeft * additionalIEZoom +
            additionaloffset +
            (canvasScrollLeft * additionalIEZoom - canvasScrollLeft))
        b.y =
          b.y -
          (corrSVG.f -
            jQuery('#canvasSection').offset().top * additionalIEZoom +
            (canvasScrollTop * additionalIEZoom - canvasScrollTop))
      }

      // Fit to SVG-Coordinates
      a.x /= corrSVG.a
      a.y /= corrSVG.d
      b.x /= corrSVG.a
      b.y /= corrSVG.d

      // Calculate the elements from the childs of the canvas
      var elements = this.facade
        .getCanvas()
        .getChildShapes(true)
        .findAll(function(value) {
          var absBounds = value.absoluteBounds()

          var bA = absBounds.upperLeft()
          var bB = absBounds.lowerRight()

          if (bA.x > a.x && bA.y > a.y && bB.x < b.x && bB.y < b.y) return true
          return false
        })

      // Set the selection
      this.facade.setSelection(elements)
    }
  },

  handleMouseMove: function(event) {
    // Calculate the size
    var size = {
      width: Event.pointerX(event) - this.position.x - jQuery('#canvasSection').offset().left,
      height: Event.pointerY(event) - this.position.y - jQuery('#canvasSection').offset().top + 5
    }

    var scrollNode = this.facade.getCanvas().rootNode.parentNode.parentNode
    size.width -= this.offsetScroll.x - scrollNode.scrollLeft
    size.height -= this.offsetScroll.y - scrollNode.scrollTop

    // Set the size
    this.resize(size)

    Event.stop(event)
  },

  hide: function() {
    this.node.style.display = 'none'
  },

  show: function() {
    this.node.style.display = ''
  },

  setPos: function(pos) {
    // Set the Position
    this.node.style.top = pos.y + 'px'
    this.node.style.left = pos.x + 'px'
    this.position = pos
  },

  resize: function(size) {
    // Calculate the negative offset
    this.setPos(this.position)
    this.size = Object.clone(size)

    if (size.width < 0) {
      this.node.style.left = this.position.x + size.width + 'px'
      size.width = -size.width
    }
    if (size.height < 0) {
      this.node.style.top = this.position.y + size.height + 'px'
      size.height = -size.height
    }

    // Set the size
    this.node.style.width = size.width + 'px'
    this.node.style.height = size.height + 'px'
  }
})

/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

if (!ORYX.Plugins) ORYX.Plugins = new Object()

ORYX.Plugins.ShapeHighlighting = Clazz.extend({
  construct: function(facade) {
    this.parentNode = facade.getCanvas().getSvgContainer()

    // The parent Node
    this.node = ORYX.Editor.graft('http://www.w3.org/2000/svg', this.parentNode, ['g'])

    this.highlightNodes = {}

    facade.registerOnEvent(ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW, this.setHighlight.bind(this))
    facade.registerOnEvent(ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE, this.hideHighlight.bind(this))
  },

  setHighlight: function(options) {
    if (options && options.highlightId) {
      var node = this.highlightNodes[options.highlightId]

      if (!node) {
        node = ORYX.Editor.graft('http://www.w3.org/2000/svg', this.node, [
          'path',
          {
            'stroke-width': 2.0,
            fill: 'none'
          }
        ])

        this.highlightNodes[options.highlightId] = node
      }

      if (options.elements && options.elements.length > 0) {
        this.setAttributesByStyle(node, options)
        this.show(node)
      } else {
        this.hide(node)
      }
    }
  },

  hideHighlight: function(options) {
    if (options && options.highlightId && this.highlightNodes[options.highlightId]) {
      this.hide(this.highlightNodes[options.highlightId])
    }
  },

  hide: function(node) {
    node.setAttributeNS(null, 'display', 'none')
  },

  show: function(node) {
    node.setAttributeNS(null, 'display', '')
  },

  setAttributesByStyle: function(node, options) {
    // If the style say, that it should look like a rectangle
    if (options.style && options.style == ORYX.CONFIG.SELECTION_HIGHLIGHT_STYLE_RECTANGLE) {
      // Set like this
      var bo = options.elements[0].absoluteBounds()

      var strWidth = options.strokewidth ? options.strokewidth : ORYX.CONFIG.BORDER_OFFSET

      node.setAttributeNS(null, 'd', this.getPathRectangle(bo.a, bo.b, strWidth))
      node.setAttributeNS(null, 'stroke', options.color ? options.color : ORYX.CONFIG.SELECTION_HIGHLIGHT_COLOR)
      node.setAttributeNS(null, 'stroke-opacity', options.opacity ? options.opacity : 0.2)
      node.setAttributeNS(null, 'stroke-width', strWidth)
    } else if (
      options.elements.length == 1 &&
      options.elements[0] instanceof ORYX.Core.Edge &&
      options.highlightId != 'selection'
    ) {
      /* Highlight containment of edge's childs */
      var path = this.getPathEdge(options.elements[0].dockers)
      if (path && path.length > 0) {
        node.setAttributeNS(null, 'd', path)
      }
      node.setAttributeNS(null, 'stroke', options.color ? options.color : ORYX.CONFIG.SELECTION_HIGHLIGHT_COLOR)
      node.setAttributeNS(null, 'stroke-opacity', options.opacity ? options.opacity : 0.2)
      node.setAttributeNS(null, 'stroke-width', ORYX.CONFIG.OFFSET_EDGE_BOUNDS)
    } else {
      // If not, set just the corners
      var path = this.getPathByElements(options.elements)
      if (path && path.length > 0) {
        node.setAttributeNS(null, 'd', path)
      }
      node.setAttributeNS(null, 'stroke', options.color ? options.color : ORYX.CONFIG.SELECTION_HIGHLIGHT_COLOR)
      node.setAttributeNS(null, 'stroke-opacity', options.opacity ? options.opacity : 1.0)
      node.setAttributeNS(null, 'stroke-width', options.strokewidth ? options.strokewidth : 2.0)
    }
  },

  getPathByElements: function(elements) {
    if (!elements || elements.length <= 0) {
      return undefined
    }

    // Get the padding and the size
    var padding = ORYX.CONFIG.SELECTED_AREA_PADDING

    var path = ''

    // Get thru all Elements
    elements.each(
      function(element) {
        if (!element) {
          return
        }
        // Get the absolute Bounds and the two Points
        var bounds = element.absoluteBounds()
        bounds.widen(padding)
        var a = bounds.upperLeft()
        var b = bounds.lowerRight()

        path = path + this.getPath(a, b)
      }.bind(this)
    )

    return path
  },

  getPath: function(a, b) {
    return this.getPathCorners(a, b)
  },

  getPathCorners: function(a, b) {
    var size = ORYX.CONFIG.SELECTION_HIGHLIGHT_SIZE

    var path = ''

    // Set: Upper left
    path = path + 'M' + a.x + ' ' + (a.y + size) + ' l0 -' + size + ' l' + size + ' 0 '
    // Set: Lower left
    path = path + 'M' + a.x + ' ' + (b.y - size) + ' l0 ' + size + ' l' + size + ' 0 '
    // Set: Lower right
    path = path + 'M' + b.x + ' ' + (b.y - size) + ' l0 ' + size + ' l-' + size + ' 0 '
    // Set: Upper right
    path = path + 'M' + b.x + ' ' + (a.y + size) + ' l0 -' + size + ' l-' + size + ' 0 '

    return path
  },

  getPathRectangle: function(a, b, strokeWidth) {
    var size = ORYX.CONFIG.SELECTION_HIGHLIGHT_SIZE

    var path = ''
    var offset = strokeWidth / 2.0

    // Set: Upper left
    path = path + 'M' + (a.x + offset) + ' ' + a.y
    path = path + ' L' + (a.x + offset) + ' ' + (b.y - offset)
    path = path + ' L' + (b.x - offset) + ' ' + (b.y - offset)
    path = path + ' L' + (b.x - offset) + ' ' + (a.y + offset)
    path = path + ' L' + (a.x + offset) + ' ' + (a.y + offset)

    return path
  },

  getPathEdge: function(edgeDockers) {
    var length = edgeDockers.length
    var path = 'M' + edgeDockers[0].bounds.center().x + ' ' + edgeDockers[0].bounds.center().y

    for (i = 1; i < length; i++) {
      var dockerPoint = edgeDockers[i].bounds.center()
      path = path + ' L' + dockerPoint.x + ' ' + dockerPoint.y
    }

    return path
  }
})

ORYX.Plugins.HighlightingSelectedShapes = Clazz.extend({
  construct: function(facade) {
    this.facade = facade
    this.opacityFull = 0.9
    this.opacityLow = 0.4

    // Register on Dragging-Events for show/hide of ShapeMenu
    //this.facade.registerOnEvent(ORYX.CONFIG.EVENT_DRAGDROP_START, this.hide.bind(this));
    //this.facade.registerOnEvent(ORYX.CONFIG.EVENT_DRAGDROP_END,  this.show.bind(this));
  },

  /**
   * On the Selection-Changed
   *
   */
  onSelectionChanged: function(event) {
    if (event.elements && event.elements.length > 1) {
      this.facade.raiseEvent({
        type: ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW,
        highlightId: 'selection',
        elements: event.elements.without(event.subSelection),
        color: ORYX.CONFIG.SELECTION_HIGHLIGHT_COLOR,
        opacity: !event.subSelection ? this.opacityFull : this.opacityLow
      })

      if (event.subSelection) {
        this.facade.raiseEvent({
          type: ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW,
          highlightId: 'subselection',
          elements: [event.subSelection],
          color: ORYX.CONFIG.SELECTION_HIGHLIGHT_COLOR,
          opacity: this.opacityFull
        })
      } else {
        this.facade.raiseEvent({ type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE, highlightId: 'subselection' })
      }
    } else {
      this.facade.raiseEvent({ type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE, highlightId: 'selection' })
      this.facade.raiseEvent({ type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE, highlightId: 'subselection' })
    }
  }
})
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

if (!ORYX.Plugins) ORYX.Plugins = new Object()

ORYX.Plugins.Overlay = Clazz.extend({
  facade: undefined,

  styleNode: undefined,

  construct: function(facade) {
    this.facade = facade

    this.changes = []

    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_OVERLAY_SHOW, this.show.bind(this))
    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_OVERLAY_HIDE, this.hide.bind(this))

    this.styleNode = document.createElement('style')
    this.styleNode.setAttributeNS(null, 'type', 'text/css')

    document.getElementsByTagName('head')[0].appendChild(this.styleNode)
  },

  /**
   * Show the overlay for specific nodes
   * @param {Object} options
   *
   * 	String				options.id		- MUST - Define the id of the overlay (is needed for the hiding of this overlay)
   *	ORYX.Core.Shape[] 	options.shapes 	- MUST - Define the Shapes for the changes
   * 	attr-name:value		options.changes	- Defines all the changes which should be shown
   *
   *
   */
  show: function(options) {
    // Checks if all arguments are available
    if (
      !options ||
      !options.shapes ||
      !options.shapes instanceof Array ||
      !options.id ||
      !options.id instanceof String ||
      options.id.length == 0
    ) {
      return
    }

    //if( this.changes[options.id]){
    //	this.hide( options )
    //}

    // Checked if attributes are setted
    if (options.attributes) {
      // FOR EACH - Shape
      options.shapes.each(
        function(el) {
          // Checks if the node is a Shape
          if (!el instanceof ORYX.Core.Shape) {
            return
          }

          this.setAttributes(el.node, options.attributes)
        }.bind(this)
      )
    }

    var isSVG = true
    try {
      isSVG = options.node && options.node instanceof SVGElement
    } catch (e) {}

    // Checks if node is setted and if this is an SVGElement
    if (options.node && isSVG) {
      options['_temps'] = []

      // FOR EACH - Node
      options.shapes.each(
        function(el, index) {
          // Checks if the node is a Shape
          if (!el instanceof ORYX.Core.Shape) {
            return
          }

          var _temp = {}
          _temp.svg = options.dontCloneNode ? options.node : options.node.cloneNode(true)

          // Add the svg node to the ORYX-Shape
          el.node.firstChild.appendChild(_temp.svg)

          // If
          if (el instanceof ORYX.Core.Edge && !options.nodePosition) {
            options['nodePosition'] = 'START'
          }

          // If the node position is setted, it has to be transformed
          if (options.nodePosition) {
            var b = el.bounds
            var p = options.nodePosition.toUpperCase()

            // Check the values of START and END
            if (el instanceof ORYX.Core.Node && p == 'START') {
              p = 'NW'
            } else if (el instanceof ORYX.Core.Node && p == 'END') {
              p = 'SE'
            } else if (el instanceof ORYX.Core.Edge && p == 'START') {
              b = el.getDockers().first().bounds
            } else if (el instanceof ORYX.Core.Edge && p == 'END') {
              b = el.getDockers().last().bounds
            }

            // Create a callback for the changing the position
            // depending on the position string
            _temp.callback = function() {
              var x = 0
              var y = 0

              if (p == 'NW') {
                // Do Nothing
              } else if (p == 'N') {
                x = b.width() / 2
              } else if (p == 'NE') {
                x = b.width()
              } else if (p == 'E') {
                x = b.width()
                y = b.height() / 2
              } else if (p == 'SE') {
                x = b.width()
                y = b.height()
              } else if (p == 'S') {
                x = b.width() / 2
                y = b.height()
              } else if (p == 'SW') {
                y = b.height()
              } else if (p == 'W') {
                y = b.height() / 2
              } else if (p == 'START' || p == 'END') {
                x = b.width() / 2
                y = b.height() / 2
              } else {
                return
              }

              if (el instanceof ORYX.Core.Edge) {
                x += b.upperLeft().x
                y += b.upperLeft().y
              }

              _temp.svg.setAttributeNS(null, 'transform', 'translate(' + x + ', ' + y + ')')
            }.bind(this)

            _temp.element = el
            _temp.callback()

            b.registerCallback(_temp.callback)
          }

          options._temps.push(_temp)
        }.bind(this)
      )
    }

    // Store the changes
    if (!this.changes[options.id]) {
      this.changes[options.id] = []
    }

    this.changes[options.id].push(options)
  },

  /**
   * Hide the overlay with the spefic id
   * @param {Object} options
   */
  hide: function(options) {
    // Checks if all arguments are available
    if (
      !options ||
      !options.id ||
      !options.id instanceof String ||
      options.id.length == 0 ||
      !this.changes[options.id]
    ) {
      return
    }

    // Delete all added attributes
    // FOR EACH - Shape
    this.changes[options.id].each(
      function(option) {
        option.shapes.each(
          function(el, index) {
            // Checks if the node is a Shape
            if (!el instanceof ORYX.Core.Shape) {
              return
            }

            this.deleteAttributes(el.node)
          }.bind(this)
        )

        if (option._temps) {
          option._temps.each(
            function(tmp) {
              // Delete the added Node, if there is one
              if (tmp.svg && tmp.svg.parentNode) {
                tmp.svg.parentNode.removeChild(tmp.svg)
              }

              // If
              if (tmp.callback && tmp.element) {
                // It has to be unregistered from the edge
                tmp.element.bounds.unregisterCallback(tmp.callback)
              }
            }.bind(this)
          )
        }
      }.bind(this)
    )

    this.changes[options.id] = null
  },

  /**
   * Set the given css attributes to that node
   * @param {HTMLElement} node
   * @param {Object} attributes
   */
  setAttributes: function(node, attributes) {
    // Get all the childs from ME
    var childs = this.getAllChilds(node.firstChild.firstChild)

    var ids = []

    // Add all Attributes which have relation to another node in this document and concate the pure id out of it
    // This is for example important for the markers of a edge
    childs.each(function(e) {
      ids.push(
        $A(e.attributes).findAll(function(attr) {
          return attr.nodeValue.startsWith('url(#')
        })
      )
    })
    ids = ids.flatten().compact()
    ids = ids
      .collect(function(s) {
        return s.nodeValue
      })
      .uniq()
    ids = ids.collect(function(s) {
      return s.slice(5, s.length - 1)
    })

    // Add the node ID to the id
    ids.unshift(node.id + ' .me')

    var attr = $H(attributes)
    var attrValue = attr
      .toJSON()
      .gsub(',', ';')
      .gsub('"', '')
    var attrMarkerValue = attributes.stroke
      ? attrValue.slice(0, attrValue.length - 1) + '; fill:' + attributes.stroke + ';}'
      : attrValue
    var attrTextValue
    if (attributes.fill) {
      var copyAttr = Object.clone(attributes)
      copyAttr.fill = 'black'
      attrTextValue = $H(copyAttr)
        .toJSON()
        .gsub(',', ';')
        .gsub('"', '')
    }

    // Create the CSS-Tags Style out of the ids and the attributes
    csstags = ids.collect(function(s, i) {
      return (
        '#' +
        s +
        ' * ' +
        (!i ? attrValue : attrMarkerValue) +
        '' +
        (attrTextValue ? ' #' + s + ' text * ' + attrTextValue : '')
      )
    })

    // Join all the tags
    var s = csstags.join(' ') + '\n'

    // And add to the end of the style tag
    this.styleNode.appendChild(document.createTextNode(s))
  },

  /**
   * Deletes all attributes which are
   * added in a special style sheet for that node
   * @param {HTMLElement} node
   */
  deleteAttributes: function(node) {
    // Get all children which contains the node id
    var delEl = $A(this.styleNode.childNodes).findAll(function(e) {
      return e.textContent.include('#' + node.id)
    })

    // Remove all of them
    delEl.each(function(el) {
      el.parentNode.removeChild(el)
    })
  },

  getAllChilds: function(node) {
    var childs = $A(node.childNodes)

    $A(node.childNodes).each(
      function(e) {
        childs.push(this.getAllChilds(e))
      }.bind(this)
    )

    return childs.flatten()
  }
})
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

if (!ORYX.Plugins) ORYX.Plugins = new Object()

ORYX.Plugins.KeysMove = ORYX.Plugins.AbstractPlugin.extend({
  facade: undefined,

  construct: function(facade) {
    this.facade = facade
    this.copyElements = []

    //this.facade.registerOnEvent(ORYX.CONFIG.EVENT_KEYDOWN, this.keyHandler.bind(this));

    // SELECT ALL
    this.facade.offer({
      keyCodes: [
        {
          metaKeys: [ORYX.CONFIG.META_KEY_META_CTRL],
          keyCode: 65,
          keyAction: ORYX.CONFIG.KEY_ACTION_DOWN
        }
      ],
      functionality: this.selectAll.bind(this)
    })

    // MOVE LEFT SMALL
    this.facade.offer({
      keyCodes: [
        {
          metaKeys: [ORYX.CONFIG.META_KEY_META_CTRL],
          keyCode: ORYX.CONFIG.KEY_CODE_LEFT,
          keyAction: ORYX.CONFIG.KEY_ACTION_DOWN
        }
      ],
      functionality: this.move.bind(this, ORYX.CONFIG.KEY_CODE_LEFT, false)
    })

    // MOVE LEFT
    this.facade.offer({
      keyCodes: [
        {
          keyCode: ORYX.CONFIG.KEY_CODE_LEFT,
          keyAction: ORYX.CONFIG.KEY_ACTION_DOWN
        }
      ],
      functionality: this.move.bind(this, ORYX.CONFIG.KEY_CODE_LEFT, true)
    })

    // MOVE RIGHT SMALL
    this.facade.offer({
      keyCodes: [
        {
          metaKeys: [ORYX.CONFIG.META_KEY_META_CTRL],
          keyCode: ORYX.CONFIG.KEY_CODE_RIGHT,
          keyAction: ORYX.CONFIG.KEY_ACTION_DOWN
        }
      ],
      functionality: this.move.bind(this, ORYX.CONFIG.KEY_CODE_RIGHT, false)
    })

    // MOVE RIGHT
    this.facade.offer({
      keyCodes: [
        {
          keyCode: ORYX.CONFIG.KEY_CODE_RIGHT,
          keyAction: ORYX.CONFIG.KEY_ACTION_DOWN
        }
      ],
      functionality: this.move.bind(this, ORYX.CONFIG.KEY_CODE_RIGHT, true)
    })

    // MOVE UP SMALL
    this.facade.offer({
      keyCodes: [
        {
          metaKeys: [ORYX.CONFIG.META_KEY_META_CTRL],
          keyCode: ORYX.CONFIG.KEY_CODE_UP,
          keyAction: ORYX.CONFIG.KEY_ACTION_DOWN
        }
      ],
      functionality: this.move.bind(this, ORYX.CONFIG.KEY_CODE_UP, false)
    })

    // MOVE UP
    this.facade.offer({
      keyCodes: [
        {
          keyCode: ORYX.CONFIG.KEY_CODE_UP,
          keyAction: ORYX.CONFIG.KEY_ACTION_DOWN
        }
      ],
      functionality: this.move.bind(this, ORYX.CONFIG.KEY_CODE_UP, true)
    })

    // MOVE DOWN SMALL
    this.facade.offer({
      keyCodes: [
        {
          metaKeys: [ORYX.CONFIG.META_KEY_META_CTRL],
          keyCode: ORYX.CONFIG.KEY_CODE_DOWN,
          keyAction: ORYX.CONFIG.KEY_ACTION_DOWN
        }
      ],
      functionality: this.move.bind(this, ORYX.CONFIG.KEY_CODE_DOWN, false)
    })

    // MOVE DOWN
    this.facade.offer({
      keyCodes: [
        {
          keyCode: ORYX.CONFIG.KEY_CODE_DOWN,
          keyAction: ORYX.CONFIG.KEY_ACTION_DOWN
        }
      ],
      functionality: this.move.bind(this, ORYX.CONFIG.KEY_CODE_DOWN, true)
    })
  },

  /**
   * Select all shapes in the editor
   *
   */
  selectAll: function(e) {
    Event.stop(e.event)
    this.facade.setSelection(this.facade.getCanvas().getChildShapes(true))
  },

  move: function(key, far, e) {
    Event.stop(e.event)

    // calculate the distance to move the objects and get the selection.
    var distance = far ? 20 : 5
    var selection = this.facade.getSelection()
    var currentSelection = this.facade.getSelection()
    var p = { x: 0, y: 0 }

    // switch on the key pressed and populate the point to move by.
    switch (key) {
      case ORYX.CONFIG.KEY_CODE_LEFT:
        p.x = -1 * distance
        break
      case ORYX.CONFIG.KEY_CODE_RIGHT:
        p.x = distance
        break
      case ORYX.CONFIG.KEY_CODE_UP:
        p.y = -1 * distance
        break
      case ORYX.CONFIG.KEY_CODE_DOWN:
        p.y = distance
        break
    }

    // move each shape in the selection by the point calculated and update it.
    selection = selection.findAll(function(shape) {
      // Check if this shape is docked to an shape in the selection
      if (
        shape instanceof ORYX.Core.Node &&
        shape.dockers.length == 1 &&
        selection.include(shape.dockers.first().getDockedShape())
      ) {
        return false
      }

      // Check if any of the parent shape is included in the selection
      var s = shape.parent
      do {
        if (selection.include(s)) {
          return false
        }
      } while ((s = s.parent))

      // Otherwise, return true
      return true
    })

    /* Edges must not be movable, if only edges are selected and at least
     * one of them is docked.
     */
    var edgesMovable = true
    var onlyEdgesSelected = selection.all(function(shape) {
      if (shape instanceof ORYX.Core.Edge) {
        if (shape.isDocked()) {
          edgesMovable = false
        }
        return true
      }
      return false
    })

    if (onlyEdgesSelected && !edgesMovable) {
      /* Abort moving shapes */
      return
    }

    selection = selection
      .map(function(shape) {
        if (shape instanceof ORYX.Core.Node) {
          /*if( shape.dockers.length == 1 ){
					return shape.dockers.first()
				} else {*/
          return shape
          //}
        } else if (shape instanceof ORYX.Core.Edge) {
          var dockers = shape.dockers

          if (selection.include(shape.dockers.first().getDockedShape())) {
            dockers = dockers.without(shape.dockers.first())
          }

          if (selection.include(shape.dockers.last().getDockedShape())) {
            dockers = dockers.without(shape.dockers.last())
          }

          return dockers
        } else {
          return null
        }
      })
      .flatten()
      .compact()

    if (selection.size() > 0) {
      //Stop moving at canvas borders
      var selectionBounds = [
        this.facade.getCanvas().bounds.lowerRight().x,
        this.facade.getCanvas().bounds.lowerRight().y,
        0,
        0
      ]
      selection.each(function(s) {
        selectionBounds[0] = Math.min(selectionBounds[0], s.bounds.upperLeft().x)
        selectionBounds[1] = Math.min(selectionBounds[1], s.bounds.upperLeft().y)
        selectionBounds[2] = Math.max(selectionBounds[2], s.bounds.lowerRight().x)
        selectionBounds[3] = Math.max(selectionBounds[3], s.bounds.lowerRight().y)
      })
      if (selectionBounds[0] + p.x < 0) p.x = -selectionBounds[0]
      if (selectionBounds[1] + p.y < 0) p.y = -selectionBounds[1]
      if (selectionBounds[2] + p.x > this.facade.getCanvas().bounds.lowerRight().x)
        p.x = this.facade.getCanvas().bounds.lowerRight().x - selectionBounds[2]
      if (selectionBounds[3] + p.y > this.facade.getCanvas().bounds.lowerRight().y)
        p.y = this.facade.getCanvas().bounds.lowerRight().y - selectionBounds[3]

      if (p.x != 0 || p.y != 0) {
        // Instantiate the moveCommand
        var commands = [new ORYX.Core.Command.Move(selection, p, null, currentSelection, this)]
        // Execute the commands
        this.facade.executeCommands(commands)
      }
    }
  },

  getUndockedCommant: function(shapes) {
    var undockEdgeCommand = ORYX.Core.Command.extend({
      construct: function(moveShapes) {
        this.dockers = moveShapes
          .collect(function(shape) {
            return shape instanceof ORYX.Core.Controls.Docker
              ? { docker: shape, dockedShape: shape.getDockedShape(), refPoint: shape.referencePoint }
              : undefined
          })
          .compact()
      },
      execute: function() {
        this.dockers.each(function(el) {
          el.docker.setDockedShape(undefined)
        })
      },
      rollback: function() {
        this.dockers.each(function(el) {
          el.docker.setDockedShape(el.dockedShape)
          el.docker.setReferencePoint(el.refPoint)
          //el.docker.update();
        })
      }
    })

    command = new undockEdgeCommand(shapes)
    command.execute()
    return command
  }

  //    /**
  //     * The key handler for this plugin. Every action from the set of cut, copy,
  //     * paste and delete should be accessible trough simple keyboard shortcuts.
  //     * This method checks whether any event triggers one of those actions.
  //     *
  //     * @param {Object} event The keyboard event that should be analysed for
  //     *     triggering of this plugin.
  //     */
  //    keyHandler: function(event){
  //        //TODO document what event.which is.
  //
  //        ORYX.Log.debug("keysMove.js handles a keyEvent.");
  //
  //        // assure we have the current event.
  //        if (!event)
  //            event = window.event;
  //
  //        // get the currently pressed key and state of control key.
  //        var pressedKey = event.which || event.keyCode;
  //        var ctrlPressed = event.ctrlKey;
  //
  //		// if the key is one of the arrow keys, forward to move and return.
  //		if ([ORYX.CONFIG.KEY_CODE_LEFT, ORYX.CONFIG.KEY_CODE_RIGHT,
  //			ORYX.CONFIG.KEY_CODE_UP, ORYX.CONFIG.KEY_CODE_DOWN].include(pressedKey)) {
  //
  //			this.move(pressedKey, !ctrlPressed);
  //			return;
  //		}
  //
  //    }
})
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

if (!ORYX.Plugins) {
  ORYX.Plugins = {}
}
if (!ORYX.Plugins.Layouter) {
  ORYX.Plugins.Layouter = {}
}

new (function() {
  /**
   * Edge layouter is an implementation to layout an edge
   * @class ORYX.Plugins.Layouter.EdgeLayouter
   * @author Willi Tscheschner
   */
  ORYX.Plugins.Layouter.EdgeLayouter = ORYX.Plugins.AbstractLayouter.extend({
    /**
     * Layout only Edges
     */
    layouted: [
      'http://b3mn.org/stencilset/bpmn1.1#SequenceFlow',
      'http://b3mn.org/stencilset/bpmn1.1#MessageFlow',
      'http://b3mn.org/stencilset/timjpdl3#SequenceFlow',
      'http://b3mn.org/stencilset/jbpm4#SequenceFlow',
      'http://b3mn.org/stencilset/bpmn2.0#MessageFlow',
      'http://b3mn.org/stencilset/bpmn2.0#SequenceFlow',
      'http://b3mn.org/stencilset/bpmn2.0choreography#MessageFlow',
      'http://b3mn.org/stencilset/bpmn2.0choreography#SequenceFlow',
      'http://b3mn.org/stencilset/bpmn2.0conversation#ConversationLink',
      'http://b3mn.org/stencilset/epc#ControlFlow',
      'http://www.signavio.com/stencilsets/processmap#ProcessLink',
      'http://www.signavio.com/stencilsets/organigram#connection'
    ],

    /**
     * Layout a set on edges
     * @param {Object} edges
     */
    layout: function(edges) {
      edges.each(
        function(edge) {
          this.doLayout(edge)
        }.bind(this)
      )
    },

    /**
     * Layout one edge
     * @param {Object} edge
     */
    doLayout: function(edge) {
      // Get from and to node
      var from = edge.getIncomingNodes()[0]
      var to = edge.getOutgoingNodes()[0]

      // Return if one is null
      if (!from || !to) {
        return
      }

      var positions = this.getPositions(from, to, edge)

      if (positions.length > 0) {
        this.setDockers(edge, positions[0].a, positions[0].b)
      }
    },

    /**
     * Returns a set on positions which are not containt either
     * in the bounds in from or to.
     * @param {Object} from Shape where the edge is come from
     * @param {Object} to Shape where the edge is leading to
     * @param {Object} edge Edge between from and to
     */
    getPositions: function(from, to, edge) {
      // Get absolute bounds
      var ab = from.absoluteBounds()
      var bb = to.absoluteBounds()

      // Get center from and to
      var a = ab.center()
      var b = bb.center()

      var am = ab.midPoint()
      var bm = bb.midPoint()

      // Get first and last reference point
      var first = Object.clone(edge.dockers.first().referencePoint)
      var last = Object.clone(edge.dockers.last().referencePoint)
      // Get the absolute one
      var aFirst = edge.dockers.first().getAbsoluteReferencePoint()
      var aLast = edge.dockers.last().getAbsoluteReferencePoint()

      // IF ------>
      // or  |
      //     V
      // Do nothing
      if (Math.abs(aFirst.x - aLast.x) < 1 || Math.abs(aFirst.y - aLast.y) < 1) {
        return []
      }

      // Calc center position, between a and b
      // depending on there weight
      var m = {}
      m.x =
        a.x < b.x
          ? (b.x - bb.width() / 2 - (a.x + ab.width() / 2)) / 2 + (a.x + ab.width() / 2)
          : (a.x - ab.width() / 2 - (b.x + bb.width() / 2)) / 2 + (b.x + bb.width() / 2)

      m.y =
        a.y < b.y
          ? (b.y - bb.height() / 2 - (a.y + ab.height() / 2)) / 2 + (a.y + ab.height() / 2)
          : (a.y - ab.height() / 2 - (b.y + bb.height() / 2)) / 2 + (b.y + bb.height() / 2)

      // Enlarge both bounds with 10
      ab.widen(5) // Wide the from less than
      bb.widen(20) // the to because of the arrow from the edge

      var positions = []
      var off = this.getOffset.bind(this)

      // Checks ----+
      //            |
      //            V
      if (!ab.isIncluded(b.x, a.y) && !bb.isIncluded(b.x, a.y)) {
        positions.push({
          a: { x: b.x + off(last, bm, 'x'), y: a.y + off(first, am, 'y') },
          z: this.getWeight(from, a.x < b.x ? 'r' : 'l', to, a.y < b.y ? 't' : 'b', edge)
        })
      }

      // Checks |
      //        +--->
      if (!ab.isIncluded(a.x, b.y) && !bb.isIncluded(a.x, b.y)) {
        positions.push({
          a: { x: a.x + off(first, am, 'x'), y: b.y + off(last, bm, 'y') },
          z: this.getWeight(from, a.y < b.y ? 'b' : 't', to, a.x < b.x ? 'l' : 'r', edge)
        })
      }

      // Checks  --+
      //           |
      //           +--->
      if (!ab.isIncluded(m.x, a.y) && !bb.isIncluded(m.x, b.y)) {
        positions.push({
          a: { x: m.x, y: a.y + off(first, am, 'y') },
          b: { x: m.x, y: b.y + off(last, bm, 'y') },
          z: this.getWeight(from, 'r', to, 'l', edge, a.x > b.x)
        })
      }

      // Checks |
      //        +---+
      //            |
      //            V
      if (!ab.isIncluded(a.x, m.y) && !bb.isIncluded(b.x, m.y)) {
        positions.push({
          a: { x: a.x + off(first, am, 'x'), y: m.y },
          b: { x: b.x + off(last, bm, 'x'), y: m.y },
          z: this.getWeight(from, 'b', to, 't', edge, a.y > b.y)
        })
      }

      // Sort DESC of weights
      return positions.sort(function(a, b) {
        return a.z < b.z ? 1 : a.z == b.z ? -1 : -1
      })
    },

    /**
     * Returns a offset for the pos to the center of the bounds
     *
     * @param {Object} val
     * @param {Object} pos2
     * @param {String} dir Direction x|y
     */
    getOffset: function(pos, pos2, dir) {
      return pos[dir] - pos2[dir]
    },

    /**
     * Returns a value which shows the weight for this configuration
     *
     * @param {Object} from Shape which is coming from
     * @param {String} d1 Direction where is goes
     * @param {Object} to Shape which goes to
     * @param {String} d2 Direction where it comes to
     * @param {Object} edge Edge between from and to
     * @param {Boolean} reverse Reverse the direction (e.g. "r" -> "l")
     */
    getWeight: function(from, d1, to, d2, edge, reverse) {
      d1 = (d1 || '').toLowerCase()
      d2 = (d2 || '').toLowerCase()

      if (!['t', 'r', 'b', 'l'].include(d1)) {
        d1 = 'r'
      }
      if (!['t', 'r', 'b', 'l'].include(d2)) {
        d1 = 'l'
      }

      // If reverse is set
      if (reverse) {
        // Reverse d1 and d2
        d1 = d1 == 't' ? 'b' : d1 == 'r' ? 'l' : d1 == 'b' ? 't' : d1 == 'l' ? 'r' : 'r'
        d2 = d2 == 't' ? 'b' : d2 == 'r' ? 'l' : d2 == 'b' ? 't' : d2 == 'l' ? 'r' : 'r'
      }

      var weight = 0
      // Get rules for from "out" and to "in"
      var dr1 = this.facade.getRules().getLayoutingRules(from, edge)['out']
      var dr2 = this.facade.getRules().getLayoutingRules(to, edge)['in']

      var fromWeight = dr1[d1]
      var toWeight = dr2[d2]

      /**
       * Return a true if the center 1 is in the same direction than center 2
       * @param {Object} direction
       * @param {Object} center1
       * @param {Object} center2
       */
      var sameDirection = function(direction, center1, center2) {
        switch (direction) {
          case 't':
            return Math.abs(center1.x - center2.x) < 2 && center1.y < center2.y
          case 'r':
            return center1.x > center2.x && Math.abs(center1.y - center2.y) < 2
          case 'b':
            return Math.abs(center1.x - center2.x) < 2 && center1.y > center2.y
          case 'l':
            return center1.x < center2.x && Math.abs(center1.y - center2.y) < 2
          default:
            return false
        }
      }

      // Check if there are same incoming edges from 'from'
      var sameIncomingFrom = from
        .getIncomingShapes()
        .findAll(function(a) {
          return a instanceof ORYX.Core.Edge
        })
        .any(function(e) {
          return sameDirection(d1, e.dockers[e.dockers.length - 2].bounds.center(), e.dockers.last().bounds.center())
        })

      // Check if there are same outgoing edges from 'to'
      var sameOutgoingTo = to
        .getOutgoingShapes()
        .findAll(function(a) {
          return a instanceof ORYX.Core.Edge
        })
        .any(function(e) {
          return sameDirection(d2, e.dockers[1].bounds.center(), e.dockers.first().bounds.center())
        })

      // If there are equivalent edges, set 0
      //fromWeight = sameIncomingFrom ? 0 : fromWeight;
      //toWeight = sameOutgoingTo ? 0 : toWeight;

      // Get the sum of "out" and the direction plus "in" and the direction
      return sameIncomingFrom || sameOutgoingTo ? 0 : fromWeight + toWeight
    },

    /**
     * Removes all current dockers from the node
     * (except the start and end) and adds two new
     * dockers, on the position a and b.
     * @param {Object} edge
     * @param {Object} a
     * @param {Object} b
     */
    setDockers: function(edge, a, b) {
      if (!edge) {
        return
      }

      // Remove all dockers (implicit,
      // start and end dockers will not removed)
      edge.dockers.each(function(r) {
        edge.removeDocker(r)
      })

      // For a and b (if exists), create
      // a new docker and set position
      ;[a, b].compact().each(function(pos) {
        var docker = edge.createDocker(undefined, pos)
        docker.bounds.centerMoveTo(pos)
      })

      // Update all dockers from the edge
      edge.dockers.each(function(docker) {
        docker.update()
      })

      // Update edge
      //edge.refresh();
      edge._update(true)
    }
  })
})()
/**
 * Copyright (c) 2006
 *
 * Philipp Berger, Martin Czuchra, Gero Decker, Ole Eckermann, Lutz Gericke,
 * Alexander Hold, Alexander Koglin, Oliver Kopp, Stefan Krumnow,
 * Matthias Kunze, Philipp Maschke, Falko Menge, Christoph Neijenhuis,
 * Hagen Overdick, Zhen Peng, Nicolas Peters, Kerstin Pfitzner, Daniel Polak,
 * Steffen Ryll, Kai Schlichting, Jan-Felix Schwarz, Daniel Taschik,
 * Willi Tscheschner, Björn Wagner, Sven Wagner-Boysen, Matthias Weidlich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **/

if (!ORYX.Plugins) ORYX.Plugins = new Object()

new (function() {
  ORYX.Plugins.BPMN2_0 = {
    /**
     *	Constructor
     *	@param {Object} Facade: The Facade of the Editor
     */
    construct: function(facade) {
      this.facade = facade

      this.facade.registerOnEvent(ORYX.CONFIG.EVENT_DRAGDOCKER_DOCKED, this.handleDockerDocked.bind(this))
      this.facade.registerOnEvent(ORYX.CONFIG.EVENT_PROPWINDOW_PROP_CHANGED, this.handlePropertyChanged.bind(this))
      this.facade.registerOnEvent('layout.bpmn2_0.pool', this.handleLayoutPool.bind(this))
      this.facade.registerOnEvent('layout.bpmn2_0.subprocess', this.handleSubProcess.bind(this))
      this.facade.registerOnEvent(ORYX.CONFIG.EVENT_SHAPEREMOVED, this.handleShapeRemove.bind(this))

      this.facade.registerOnEvent(ORYX.CONFIG.EVENT_LOADED, this.afterLoad.bind(this))

      this.namespace = undefined
    },

    /**
     * Force to update every pool
     */
    afterLoad: function() {
      this.facade
        .getCanvas()
        .getChildNodes()
        .each(
          function(shape) {
            if (
              shape
                .getStencil()
                .id()
                .endsWith('Pool')
            ) {
              this.handleLayoutPool({
                shape: shape
              })
            }
          }.bind(this)
        )
    },

    /**
     * If a pool is selected and contains no lane,
     * a lane is created automagically
     */
    onSelectionChanged: function(event) {
      var selection = event.elements

      if (selection && selection.length === 1) {
        var namespace = this.getNamespace()
        var shape = selection[0]
        if (shape.getStencil().idWithoutNs() === 'Pool') {
          if (shape.getChildNodes().length === 0) {
            // create a lane inside the selected pool
            var option = {
              type: namespace + 'Lane',
              position: { x: 0, y: 0 },
              namespace: shape.getStencil().namespace(),
              parent: shape
            }
            this.facade.createShape(option)
            this.facade.getCanvas().update()
            this.facade.setSelection([shape])
          }
        }
      }

      // Preventing selection of all lanes but not the pool
      if (
        selection.any(function(s) {
          return (
            s instanceof ORYX.Core.Node &&
            s
              .getStencil()
              .id()
              .endsWith('Lane')
          )
        })
      ) {
        var lanes = selection.findAll(function(s) {
          return (
            s instanceof ORYX.Core.Node &&
            s
              .getStencil()
              .id()
              .endsWith('Lane')
          )
        })

        var pools = []
        var unselectLanes = []
        lanes.each(
          function(lane) {
            pools.push(this.getParentPool(lane))
          }.bind(this)
        )

        pools = pools.uniq().findAll(
          function(pool) {
            var childLanes = this.getLanes(pool, true)
            if (
              childLanes.all(function(lane) {
                return lanes.include(lane)
              })
            ) {
              unselectLanes = unselectLanes.concat(childLanes)
              return true
            } else if (
              selection.include(pool) &&
              childLanes.any(function(lane) {
                return lanes.include(lane)
              })
            ) {
              unselectLanes = unselectLanes.concat(childLanes)
              return true
            } else {
              return false
            }
          }.bind(this)
        )

        if (unselectLanes.length > 0 && pools.length > 0) {
          selection = selection.without.apply(selection, unselectLanes)
          selection = selection.concat(pools)
          this.facade.setSelection(selection.uniq())
        }
      }
    },

    handleShapeRemove: function(option) {
      var sh = option.shape
      var parent = option.parent

      if (
        sh instanceof ORYX.Core.Node &&
        sh.getStencil().idWithoutNs() === 'Lane' &&
        this.facade.isExecutingCommands()
      ) {
        var pool = this.getParentPool(parent)
        if (pool && pool.parent) {
          var isLeafFn = function(leaf) {
            return !leaf.getChildNodes().any(function(r) {
              return r.getStencil().idWithoutNs() === 'Lane'
            })
          }

          var isLeaf = isLeafFn(sh)
          var parentHasMoreLanes = parent.getChildNodes().any(function(r) {
            return r.getStencil().idWithoutNs() === 'Lane'
          })

          if (isLeaf && parentHasMoreLanes) {
            var command = new ResizeLanesCommand(sh, parent, pool, this)
            this.facade.executeCommands([command])
          } else if (
            !isLeaf &&
            !this.facade.getSelection().any(function(select) {
              // Find one of the selection, which is a lane and child of "sh" and is a leaf lane
              return (
                select instanceof ORYX.Core.Node &&
                select.getStencil().idWithoutNs() === 'Lane' &&
                select.isParent(sh) &&
                isLeafFn(select)
              )
            })
          ) {
            var Command = ORYX.Core.Command.extend({
              construct: function(shape, facade) {
                this.children = shape.getChildNodes(true)
                this.facade = facade
              },
              execute: function() {
                this.children.each(function(child) {
                  child.bounds.moveBy(30, 0)
                })
                //this.facade.getCanvas().update();
              },
              rollback: function() {
                this.children.each(function(child) {
                  child.bounds.moveBy(-30, 0)
                })
                //this.facade.getCanvas().update();
              }
            })
            this.facade.executeCommands([new Command(sh, this.facade)])
          } else if (isLeaf && !parentHasMoreLanes && parent == pool) {
            parent.add(sh)
          }
        }
      }
    },

    hashedSubProcesses: {},

    hashChildShapes: function(shape) {
      var children = shape.getChildNodes()
      children.each(
        function(child) {
          if (this.hashedSubProcesses[child.id]) {
            this.hashedSubProcesses[child.id] = child.absoluteXY()
            this.hashedSubProcesses[child.id].width = child.bounds.width()
            this.hashedSubProcesses[child.id].height = child.bounds.height()
            this.hashChildShapes(child)
          }
        }.bind(this)
      )
    },

    /**
     * Handle the layouting of a sub process.
     * Mainly to adjust the child dockers of a sub process.
     *
     */
    handleSubProcess: function(option) {
      var sh = option.shape

      if (!this.hashedSubProcesses[sh.id]) {
        this.hashedSubProcesses[sh.id] = sh.absoluteXY()
        this.hashedSubProcesses[sh.id].width = sh.bounds.width()
        this.hashedSubProcesses[sh.id].height = sh.bounds.height()
        return
      }

      var offset = sh.absoluteXY()
      offset.x -= this.hashedSubProcesses[sh.id].x
      offset.y -= this.hashedSubProcesses[sh.id].y

      var resized =
        this.hashedSubProcesses[sh.id].width !== sh.bounds.width() ||
        this.hashedSubProcesses[sh.id].height !== sh.bounds.height()

      this.hashedSubProcesses[sh.id] = sh.absoluteXY()
      this.hashedSubProcesses[sh.id].width = sh.bounds.width()
      this.hashedSubProcesses[sh.id].height = sh.bounds.height()
      this.hashChildShapes(sh)

      // Move dockers only if currently is not resizing
      if (this.facade.isExecutingCommands() && !resized) {
        this.moveChildDockers(sh, offset)
      }
    },

    moveChildDockers: function(shape, offset) {
      if (!offset.x && !offset.y) {
        return
      }

      var children = shape.getChildNodes(true)

      // Get all nodes
      var dockers = children
        // Get all incoming and outgoing edges
        .map(function(node) {
          return [].concat(node.getIncomingShapes()).concat(node.getOutgoingShapes())
        })
        // Flatten all including arrays into one
        .flatten()
        // Get every edge only once
        .uniq()
        // Get all dockers
        .map(function(edge) {
          return edge.dockers.length > 2 ? edge.dockers.slice(1, edge.dockers.length - 1) : []
        })
        // Flatten the dockers lists
        .flatten()

      var abs = shape.absoluteBounds()
      abs.moveBy(-offset.x, -offset.y)
      var obj = {}
      dockers.each(function(docker) {
        if (docker.isChanged) {
          return
        }

        var off = Object.clone(offset)

        if (!abs.isIncluded(docker.bounds.center())) {
          var index = docker.parent.dockers.indexOf(docker)
          var size = docker.parent.dockers.length
          var from = docker.parent.getSource()
          var to = docker.parent.getTarget()

          var bothAreIncluded = children.include(from) && children.include(to)

          if (!bothAreIncluded) {
            var previousIsOver = index !== 0 ? abs.isIncluded(docker.parent.dockers[index - 1].bounds.center()) : false
            var nextIsOver =
              index !== size - 1 ? abs.isIncluded(docker.parent.dockers[index + 1].bounds.center()) : false

            if (!previousIsOver && !nextIsOver) {
              return
            }

            var ref = docker.parent.dockers[previousIsOver ? index - 1 : index + 1]
            if (Math.abs(-Math.abs(ref.bounds.center().x - docker.bounds.center().x)) < 2) {
              off.y = 0
            } else if (Math.abs(-Math.abs(ref.bounds.center().y - docker.bounds.center().y)) < 2) {
              off.x = 0
            } else {
              return
            }
          }
        }

        obj[docker.getId()] = {
          docker: docker,
          offset: off
        }
      })

      // Set dockers
      this.facade.executeCommands([new ORYX.Core.MoveDockersCommand(obj)])
    },

    /**
     * DragDocker.Docked Handler
     *
     */

    handleDockerDocked: function(options) {
      var namespace = this.getNamespace()

      var edge = options.parent
      var edgeSource = options.target

      if (edge.getStencil().id() === namespace + 'SequenceFlow') {
        var isGateway = edgeSource
          .getStencil()
          .groups()
          .find(function(group) {
            if (group == 'Gateways') return group
          })
        if (!isGateway && edge.properties['oryx-conditiontype'] == 'Expression')
          // show diamond on edge source
          edge.setProperty('oryx-showdiamondmarker', true)
        // do not show diamond on edge source
        else edge.setProperty('oryx-showdiamondmarker', false)

        // update edge rendering
        //edge.update();

        this.facade.getCanvas().update()
      }
    },

    /**
     * PropertyWindow.PropertyChanged Handler
     */
    handlePropertyChanged: function(option) {
      var namespace = this.getNamespace()

      var shapes = option.elements
      var propertyKey = option.key
      var propertyValue = option.value

      var changed = false
      shapes.each(
        function(shape) {
          if (shape.getStencil().id() === namespace + 'SequenceFlow' && propertyKey === 'oryx-conditiontype') {
            if (propertyValue != 'Expression')
              // Do not show the Diamond
              shape.setProperty('oryx-showdiamondmarker', false)
            else {
              var incomingShapes = shape.getIncomingShapes()

              if (!incomingShapes) {
                shape.setProperty('oryx-showdiamondmarker', true)
              }

              var incomingGateway = incomingShapes.find(function(aShape) {
                var foundGateway = aShape
                  .getStencil()
                  .groups()
                  .find(function(group) {
                    if (group == 'Gateways') return group
                  })
                if (foundGateway) return foundGateway
              })

              if (!incomingGateway)
                // show diamond on edge source
                shape.setProperty('oryx-showdiamondmarker', true)
              // do not show diamond
              else shape.setProperty('oryx-showdiamondmarker', false)
            }

            changed = true
          }
        }.bind(this)
      )

      if (changed) {
        this.facade.getCanvas().update()
      }
    },

    hashedPoolPositions: {},
    hashedLaneDepth: {},
    hashedBounds: {},
    hashedPositions: {},

    /**
     * Handler for layouting event 'layout.bpmn2_0.pool'
     * @param {Object} event
     */
    handleLayoutPool: function(event) {
      var pool = event.shape
      var selection = this.facade.getSelection()
      var currentShape = selection.include(pool) ? pool : selection.first()

      currentShape = currentShape || pool

      this.currentPool = pool

      // Check if it is a pool or a lane
      if (
        !(
          currentShape
            .getStencil()
            .id()
            .endsWith('Pool') ||
          currentShape
            .getStencil()
            .id()
            .endsWith('Lane')
        )
      ) {
        return
      }

      // Check if the lane is within the pool and is not removed lately
      if (currentShape !== pool && !currentShape.isParent(pool) && !this.hashedBounds[pool.id][currentShape.id]) {
        return
      }

      if (!this.hashedBounds[pool.id]) {
        this.hashedBounds[pool.id] = {}
      }

      // Find all child lanes
      var lanes = this.getLanes(pool)

      if (lanes.length <= 0) {
        return
      }

      var allLanes = this.getLanes(pool, true),
        hp
      var considerForDockers = allLanes.clone()

      var hashedPositions = new Hash()
      allLanes.each(function(lane) {
        hashedPositions.set(lane.id, lane.bounds.upperLeft())
      })

      // Show/hide caption regarding the number of lanes
      if (lanes.length === 1 && this.getLanes(lanes.first()).length <= 0) {
        // TRUE if there is a caption
        var caption =
          lanes
            .first()
            .properties.get('oryx-name')
            .trim().length > 0
        lanes.first().setProperty('oryx-showcaption', caption)
        var rect = lanes.first().node.getElementsByTagName('rect')
        rect[0].setAttributeNS(null, 'display', 'none')
      } else {
        allLanes.invoke('setProperty', 'oryx-showcaption', true)
        allLanes.each(function(lane) {
          var rect = lane.node.getElementsByTagName('rect')
          rect[0].removeAttributeNS(null, 'display')
        })
      }

      var deletedLanes = []
      var addedLanes = []

      // Get all new lanes
      var i = -1
      while (++i < allLanes.length) {
        if (!this.hashedBounds[pool.id][allLanes[i].id]) {
          addedLanes.push(allLanes[i])
        }
      }

      if (addedLanes.length > 0) {
        currentShape = addedLanes.first()
      }

      // Get all deleted lanes
      var resourceIds = $H(this.hashedBounds[pool.id]).keys()
      var i = -1
      while (++i < resourceIds.length) {
        if (
          !allLanes.any(function(lane) {
            return lane.id == resourceIds[i]
          })
        ) {
          deletedLanes.push(this.hashedBounds[pool.id][resourceIds[i]])
          selection = selection.without(function(r) {
            return r.id == resourceIds[i]
          })
        }
      }

      var height, width, x, y

      if (deletedLanes.length > 0 || addedLanes.length > 0) {
        if (addedLanes.length === 1 && this.getLanes(addedLanes[0].parent).length === 1) {
          // Set height from the pool
          height = this.adjustHeight(lanes, addedLanes[0].parent)
        } else {
          // Set height from the pool
          height = this.updateHeight(pool)
        }
        // Set width from the pool
        width = this.adjustWidth(lanes, pool.bounds.width())

        pool.update()
      } else if (pool == currentShape) {

      /**
       * Set width/height depending on the pool
       */
        if (selection.length === 1 && this.isResized(pool, this.hashedPoolPositions[pool.id])) {
          var oldXY = this.hashedPoolPositions[pool.id].upperLeft()
          var xy = pool.bounds.upperLeft()
          var scale = 0
          if (this.shouldScale(pool)) {
            var old = this.hashedPoolPositions[pool.id]
            scale = old.height() / pool.bounds.height()
          }

          this.adjustLanes(pool, allLanes, oldXY.x - xy.x, oldXY.y - xy.y, scale)
        }

        // Set height from the pool
        height = this.adjustHeight(lanes, undefined, pool.bounds.height())
        // Set width from the pool
        width = this.adjustWidth(lanes, pool.bounds.width())
      } else {

      /**‚
       * Set width/height depending on containing lanes
       */
        // Reposition the pool if one shape is selected and the upperleft has changed
        if (selection.length === 1 && this.isResized(currentShape, this.hashedBounds[pool.id][currentShape.id])) {
          var oldXY = this.hashedBounds[pool.id][currentShape.id].upperLeft()
          var xy = currentShape.absoluteXY()
          x = oldXY.x - xy.x
          y = oldXY.y - xy.y

          // Adjust all other lanes beneath this lane
          if (x || y) {
            considerForDockers = considerForDockers.without(currentShape)
            this.adjustLanes(pool, this.getAllExcludedLanes(pool, currentShape), x, 0)
          }

          // Adjust all child lanes
          var childLanes = this.getLanes(currentShape, true)
          if (childLanes.length > 0) {
            if (this.shouldScale(currentShape)) {
              var old = this.hashedBounds[pool.id][currentShape.id]
              var scale = old.height() / currentShape.bounds.height()
              this.adjustLanes(pool, childLanes, x, y, scale)
            } else {
              this.adjustLanes(pool, childLanes, x, y, 0)
            }
          }
        }

        // Cache all bounds
        var changes = allLanes.map(function(lane) {
          return {
            shape: lane,
            bounds: lane.bounds.clone()
          }
        })

        // Get height and adjust child heights
        height = this.adjustHeight(lanes, currentShape)
        // Check if something has changed and maybe create a command
        this.checkForChanges(allLanes, changes)

        // Set width from the current shape
        width = this.adjustWidth(lanes, currentShape.bounds.width() + this.getDepth(currentShape, pool) * 30)
      }

      this.setDimensions(pool, width, height, x, y)

      if (this.facade.isExecutingCommands() && (deletedLanes.length === 0 || addedLanes.length !== 0)) {
        // Update all dockers
        this.updateDockers(considerForDockers, pool)

        // Check if the order has changed
        var poolHashedPositions = this.hashedPositions[pool.id]
        if (
          poolHashedPositions &&
          poolHashedPositions.keys().any(function(key, i) {
            return (allLanes[i] || {}).id !== key
          })
        ) {
          var LanesHasBeenReordered = ORYX.Core.Command.extend({
            construct: function(originPosition, newPosition, lanes, plugin, poolId) {
              this.originPosition = Object.clone(originPosition)
              this.newPosition = Object.clone(newPosition)
              this.lanes = lanes
              this.plugin = plugin
              this.pool = poolId
            },
            execute: function() {
              if (!this.executed) {
                this.executed = true
                this.lanes.each(
                  function(lane) {
                    if (this.newPosition[lane.id]) lane.bounds.moveTo(this.newPosition[lane.id])
                  }.bind(this)
                )
                this.plugin.hashedPositions[this.pool] = Object.clone(this.newPosition)
              }
            },
            rollback: function() {
              this.lanes.each(
                function(lane) {
                  if (this.originPosition[lane.id]) lane.bounds.moveTo(this.originPosition[lane.id])
                }.bind(this)
              )
              this.plugin.hashedPositions[this.pool] = Object.clone(this.originPosition)
            }
          })

          var hp2 = new Hash()
          allLanes.each(function(lane) {
            hp2.set(lane.id, lane.bounds.upperLeft())
          })

          var command = new LanesHasBeenReordered(hashedPositions, hp2, allLanes, this, pool.id)
          this.facade.executeCommands([command])
        }
      }

      this.hashedBounds[pool.id] = {}
      this.hashedPositions[pool.id] = hashedPositions

      var i = -1
      while (++i < allLanes.length) {
        // Cache positions
        this.hashedBounds[pool.id][allLanes[i].id] = allLanes[i].absoluteBounds()

        // Cache also the bounds of child shapes, mainly for child subprocesses
        this.hashChildShapes(allLanes[i])

        this.hashedLaneDepth[allLanes[i].id] = this.getDepth(allLanes[i], pool)

        this.forceToUpdateLane(allLanes[i])
      }

      this.hashedPoolPositions[pool.id] = pool.bounds.clone()

      // Update selection
      //this.facade.setSelection(selection);
    },

    shouldScale: function(element) {
      var childLanes = element.getChildNodes().findAll(function(shape) {
        return shape
          .getStencil()
          .id()
          .endsWith('Lane')
      })
      return (
        childLanes.length > 1 ||
        childLanes.any(
          function(lane) {
            return this.shouldScale(lane)
          }.bind(this)
        )
      )
    },

    /**
     * Lookup if some bounds has changed
     * @param {Object} lanes
     * @param {Object} changes
     */
    checkForChanges: function(lanes, changes) {
      // Check if something has changed
      if (
        this.facade.isExecutingCommands() &&
        changes.any(function(change) {
          return change.shape.bounds.toString() !== change.bounds.toString()
        })
      ) {
        var Command = ORYX.Core.Command.extend({
          construct: function(changes) {
            this.oldState = changes
            this.newState = changes.map(function(s) {
              return { shape: s.shape, bounds: s.bounds.clone() }
            })
          },
          execute: function() {
            if (this.executed) {
              this.applyState(this.newState)
            }
            this.executed = true
          },
          rollback: function() {
            this.applyState(this.oldState)
          },
          applyState: function(state) {
            state.each(function(s) {
              s.shape.bounds.set(s.bounds.upperLeft(), s.bounds.lowerRight())
            })
          }
        })

        this.facade.executeCommands([new Command(changes)])
      }
    },

    isResized: function(shape, bounds) {
      if (!bounds || !shape) {
        return false
      }

      var oldB = bounds
      //var oldXY = oldB.upperLeft();
      //var xy = shape.absoluteXY();
      return (
        Math.round(oldB.width() - shape.bounds.width()) !== 0 || Math.round(oldB.height() - shape.bounds.height()) !== 0
      )
    },

    adjustLanes: function(pool, lanes, x, y, scale) {
      scale = scale || 0

      // For every lane, adjust the child nodes with the offset
      lanes.each(
        function(l) {
          l.getChildNodes().each(
            function(child) {
              if (
                !child
                  .getStencil()
                  .id()
                  .endsWith('Lane')
              ) {
                var cy = scale ? child.bounds.center().y - child.bounds.center().y / scale : -y
                child.bounds.moveBy(x || 0, -cy)

                if (
                  scale &&
                  child
                    .getStencil()
                    .id()
                    .endsWith('Subprocess')
                ) {
                  this.moveChildDockers(child, { x: 0, y: -cy })
                }
              }
            }.bind(this)
          )
          this.hashedBounds[pool.id][l.id].moveBy(-(x || 0), !scale ? -y : 0)
          if (scale) {
            l.isScaled = true
          }
        }.bind(this)
      )
    },

    getAllExcludedLanes: function(parent, lane) {
      var lanes = []
      parent.getChildNodes().each(
        function(shape) {
          if (
            (!lane || shape !== lane) &&
            shape
              .getStencil()
              .id()
              .endsWith('Lane')
          ) {
            lanes.push(shape)
            lanes = lanes.concat(this.getAllExcludedLanes(shape, lane))
          }
        }.bind(this)
      )
      return lanes
    },

    forceToUpdateLane: function(lane) {
      if (lane.bounds.height() !== lane._svgShapes[0].height) {
        lane.isChanged = true
        lane.isResized = true
        lane._update()
      }
    },

    getDepth: function(child, parent) {
      var i = 0
      while (child && child.parent && child !== parent) {
        child = child.parent
        ++i
      }
      return i
    },

    updateDepth: function(lane, fromDepth, toDepth) {
      var xOffset = (fromDepth - toDepth) * 30

      lane.getChildNodes().each(function(shape) {
        shape.bounds.moveBy(xOffset, 0)

        ;[].concat(children[j].getIncomingShapes()).concat(children[j].getOutgoingShapes())
      })
    },

    setDimensions: function(shape, width, height, x, y) {
      var isLane = shape
        .getStencil()
        .id()
        .endsWith('Lane')
      // Set the bounds
      shape.bounds.set(
        isLane ? 30 : shape.bounds.a.x - (x || 0),
        isLane ? shape.bounds.a.y : shape.bounds.a.y - (y || 0),
        width ? shape.bounds.a.x + width - (isLane ? 30 : x || 0) : shape.bounds.b.x,
        height ? shape.bounds.a.y + height - (isLane ? 0 : y || 0) : shape.bounds.b.y
      )
    },

    setLanePosition: function(shape, y) {
      shape.bounds.moveTo(30, y)
    },

    adjustWidth: function(lanes, width) {
      // Set width to each lane
      ;(lanes || []).each(
        function(lane) {
          this.setDimensions(lane, width)
          this.adjustWidth(this.getLanes(lane), width - 30)
        }.bind(this)
      )

      return width
    },

    adjustHeight: function(lanes, changedLane, propagateHeight) {
      var oldHeight = 0
      if (!changedLane && propagateHeight) {
        var i = -1
        while (++i < lanes.length) {
          oldHeight += lanes[i].bounds.height()
        }
      }

      var i = -1
      var height = 0

      // Iterate trough every lane
      while (++i < lanes.length) {
        if (lanes[i] === changedLane) {
          // Propagate new height down to the children
          this.adjustHeight(this.getLanes(lanes[i]), undefined, lanes[i].bounds.height())

          lanes[i].bounds.set(
            { x: 30, y: height },
            { x: lanes[i].bounds.width() + 30, y: lanes[i].bounds.height() + height }
          )
        } else if (!changedLane && propagateHeight) {
          var tempHeight = (lanes[i].bounds.height() * propagateHeight) / oldHeight
          // Propagate height
          this.adjustHeight(this.getLanes(lanes[i]), undefined, tempHeight)
          // Set height propotional to the propagated and old height
          this.setDimensions(lanes[i], null, tempHeight)
          this.setLanePosition(lanes[i], height)
        } else {
          // Get height from children
          var tempHeight = this.adjustHeight(this.getLanes(lanes[i]), changedLane, propagateHeight)
          if (!tempHeight) {
            tempHeight = lanes[i].bounds.height()
          }
          this.setDimensions(lanes[i], null, tempHeight)
          this.setLanePosition(lanes[i], height)
        }

        height += lanes[i].bounds.height()
      }

      return height
    },

    updateHeight: function(root) {
      var lanes = this.getLanes(root)

      if (lanes.length == 0) {
        return root.bounds.height()
      }

      var height = 0
      var i = -1
      while (++i < lanes.length) {
        this.setLanePosition(lanes[i], height)
        height += this.updateHeight(lanes[i])
      }

      this.setDimensions(root, null, height)

      return height
    },

    getOffset: function(lane, includePool, pool) {
      var offset = { x: 0, y: 0 }

      /*var parent = lane;
                 while(parent) {
                                               
                       
                        var offParent = this.hashedBounds[pool.id][parent.id] ||(includePool === true ? this.hashedPoolPositions[parent.id] : undefined);
                        if (offParent){
                                var ul = parent.bounds.upperLeft();
                                var ulo = offParent.upperLeft();
                                offset.x += ul.x-ulo.x;
                                offset.y += ul.y-ulo.y;
                        }
                       
                        if (parent.getStencil().id().endsWith("Pool")) {
                                break;
                        }
                       
                        parent = parent.parent;
                }       */

      var offset = lane.absoluteXY()

      var hashed =
        this.hashedBounds[pool.id][lane.id] || (includePool === true ? this.hashedPoolPositions[lane.id] : undefined)
      if (hashed) {
        offset.x -= hashed.upperLeft().x
        offset.y -= hashed.upperLeft().y
      } else {
        return { x: 0, y: 0 }
      }
      return offset
    },

    getNextLane: function(shape) {
      while (
        shape &&
        !shape
          .getStencil()
          .id()
          .endsWith('Lane')
      ) {
        if (shape instanceof ORYX.Core.Canvas) {
          return null
        }
        shape = shape.parent
      }
      return shape
    },

    getParentPool: function(shape) {
      while (
        shape &&
        !shape
          .getStencil()
          .id()
          .endsWith('Pool')
      ) {
        if (shape instanceof ORYX.Core.Canvas) {
          return null
        }
        shape = shape.parent
      }
      return shape
    },

    updateDockers: function(lanes, pool) {
      var absPool = pool.absoluteBounds(),
        movedShapes = []
      var oldPool = (this.hashedPoolPositions[pool.id] || absPool).clone()

      var i = -1,
        j = -1,
        k = -1,
        l = -1,
        docker
      var dockers = {}

      while (++i < lanes.length) {
        if (!this.hashedBounds[pool.id][lanes[i].id]) {
          continue
        }

        var isScaled = lanes[i].isScaled
        delete lanes[i].isScaled
        var children = lanes[i].getChildNodes()
        var absBounds = lanes[i].absoluteBounds()
        var oldBounds = this.hashedBounds[pool.id][lanes[i].id] || absBounds
        //oldBounds.moveBy((absBounds.upperLeft().x-lanes[i].bounds.upperLeft().x), (absBounds.upperLeft().y-lanes[i].bounds.upperLeft().y));
        var offset = this.getOffset(lanes[i], true, pool)
        var xOffsetDepth = 0

        var depth = this.getDepth(lanes[i], pool)
        if (this.hashedLaneDepth[lanes[i].id] !== undefined && this.hashedLaneDepth[lanes[i].id] !== depth) {
          xOffsetDepth = (this.hashedLaneDepth[lanes[i].id] - depth) * 30
          offset.x += xOffsetDepth
        }

        j = -1

        while (++j < children.length) {
          if (
            xOffsetDepth &&
            !children[j]
              .getStencil()
              .id()
              .endsWith('Lane')
          ) {
            movedShapes.push({ xOffset: xOffsetDepth, shape: children[j] })
            children[j].bounds.moveBy(xOffsetDepth, 0)
          }

          if (
            children[j]
              .getStencil()
              .id()
              .endsWith('Subprocess')
          ) {
            this.moveChildDockers(children[j], offset)
          }

          var edges = []
            .concat(children[j].getIncomingShapes())
            .concat(children[j].getOutgoingShapes())
            // Remove all edges which are included in the selection from the list
            .findAll(function(r) {
              return r instanceof ORYX.Core.Edge
            })

          k = -1
          while (++k < edges.length) {
            if (
              edges[k]
                .getStencil()
                .id()
                .endsWith('MessageFlow')
            ) {
              this.layoutEdges(children[j], [edges[k]], offset)
              continue
            }

            l = -1
            while (++l < edges[k].dockers.length) {
              docker = edges[k].dockers[l]

              if (docker.getDockedShape() || docker.isChanged) {
                continue
              }

              pos = docker.bounds.center()

              // Check if the modified center included the new position
              var isOverLane = oldBounds.isIncluded(pos)
              // Check if the original center is over the pool
              var isOutSidePool = !oldPool.isIncluded(pos)
              var previousIsOverLane =
                l == 0 ? isOverLane : oldBounds.isIncluded(edges[k].dockers[l - 1].bounds.center())
              var nextIsOverLane =
                l == edges[k].dockers.length - 1
                  ? isOverLane
                  : oldBounds.isIncluded(edges[k].dockers[l + 1].bounds.center())
              var off = Object.clone(offset)

              // If the
              if (isScaled && isOverLane && this.isResized(lanes[i], this.hashedBounds[pool.id][lanes[i].id])) {
                var relY = pos.y - absBounds.upperLeft().y + off.y
                off.y -= relY - relY * (absBounds.height() / oldBounds.height())
              }

              // Check if the previous dockers docked shape is from this lane
              // Otherwise, check if the docker is over the lane OR is outside the lane
              // but the previous/next was over this lane
              if (isOverLane) {
                dockers[docker.id] = { docker: docker, offset: off }
              }
              /*else if (l == 1 && edges[k].dockers.length>2 && edges[k].dockers[l-1].isDocked()){
                                                        var dockedLane = this.getNextLane(edges[k].dockers[l-1].getDockedShape());
                                                        if (dockedLane != lanes[i])
                                                                continue;
                                                        dockers[docker.id] = {docker: docker, offset:offset};
                                                }
                                                // Check if the next dockers docked shape is from this lane
                                                else if (l == edges[k].dockers.length-2 && edges[k].dockers.length>2 && edges[k].dockers[l+1].isDocked()){
                                                        var dockedLane = this.getNextLane(edges[k].dockers[l+1].getDockedShape());
                                                        if (dockedLane != lanes[i])
                                                                continue;
                                                        dockers[docker.id] = {docker: docker, offset:offset};
                                                }
                                                                                               
                                                else if (isOutSidePool) {
                                                        dockers[docker.id] = {docker: docker, offset:this.getOffset(lanes[i], true, pool)};
                                                }*/
            }
          }
        }
      }

      // Move the moved children
      var MoveChildCommand = ORYX.Core.Command.extend({
        construct: function(state) {
          this.state = state
        },
        execute: function() {
          if (this.executed) {
            this.state.each(function(s) {
              s.shape.bounds.moveBy(s.xOffset, 0)
            })
          }
          this.executed = true
        },
        rollback: function() {
          this.state.each(function(s) {
            s.shape.bounds.moveBy(-s.xOffset, 0)
          })
        }
      })

      // Set dockers
      this.facade.executeCommands([new ORYX.Core.MoveDockersCommand(dockers), new MoveChildCommand(movedShapes)])
    },

    moveBy: function(pos, offset) {
      pos.x += offset.x
      pos.y += offset.y
      return pos
    },

    getHashedBounds: function(shape) {
      return this.currentPool && this.hashedBounds[this.currentPool.id][shape.id]
        ? this.hashedBounds[this.currentPool.id][shape.id]
        : shape.absoluteBounds()
    },

    /**
     * Returns a set on all child lanes for the given Shape. If recursive is TRUE, also indirect children will be returned (default is FALSE)
     * The set is sorted with first child the lowest y-coordinate and the last one the highest.
     * @param {ORYX.Core.Shape} shape
     * @param {boolean} recursive
     */
    getLanes: function(shape, recursive) {
      var namespace = this.getNamespace()

      // Get all the child lanes
      var lanes = shape.getChildNodes(recursive || false).findAll(function(node) {
        return node.getStencil().id() === namespace + 'Lane'
      })

      // Sort all lanes by there y coordinate
      lanes = lanes.sort(
        function(a, b) {
          // Get y coordinates for upper left and lower right
          var auy = Math.round(a.bounds.upperLeft().y)
          var buy = Math.round(b.bounds.upperLeft().y)
          var aly = Math.round(a.bounds.lowerRight().y)
          var bly = Math.round(b.bounds.lowerRight().y)

          var ha = this.getHashedBounds(a)
          var hb = this.getHashedBounds(b)

          // Get the old y coordinates
          var oauy = Math.round(ha.upperLeft().y)
          var obuy = Math.round(hb.upperLeft().y)
          var oaly = Math.round(ha.lowerRight().y)
          var obly = Math.round(hb.lowerRight().y)

          // If equal, than use the old one
          if (auy == buy && aly == bly) {
            auy = oauy
            buy = obuy
            aly = oaly
            bly = obly
          }

          if (Math.round(a.bounds.height() - ha.height()) === 0 && Math.round(b.bounds.height() - hb.height()) === 0) {
            return auy < buy ? -1 : auy > buy ? 1 : 0
          }

          // Check if upper left and lower right is completely above/below
          var above = auy < buy && aly < bly
          var below = auy > buy && aly > bly
          // Check if a is above b including the old values
          var slightlyAboveBottom = auy < buy && aly >= bly && oaly < obly
          var slightlyAboveTop = auy >= buy && aly < bly && oauy < obuy
          // Check if a is below b including the old values
          var slightlyBelowBottom = auy > buy && aly <= bly && oaly > obly
          var slightlyBelowTop = auy <= buy && aly > bly && oauy > obuy

          // Return -1 if a is above b, 1 if b is above a, or 0 otherwise
          return above || slightlyAboveBottom || slightlyAboveTop
            ? -1
            : below || slightlyBelowBottom || slightlyBelowTop
            ? 1
            : 0
        }.bind(this)
      )

      // Return lanes
      return lanes
    },

    getNamespace: function() {
      if (!this.namespace) {
        var stencilsets = this.facade.getStencilSets()
        if (stencilsets.keys()) {
          this.namespace = stencilsets.keys()[0]
        } else {
          return undefined
        }
      }
      return this.namespace
    }
  }

  var ResizeLanesCommand = ORYX.Core.Command.extend({
    construct: function(shape, parent, pool, plugin) {
      this.facade = plugin.facade
      this.plugin = plugin
      this.shape = shape
      this.changes

      this.pool = pool

      this.parent = parent

      this.shapeChildren = []

      /*
       * The Bounds have to be stored
       * separate because they would
       * otherwise also be influenced
       */
      this.shape.getChildShapes().each(
        function(childShape) {
          this.shapeChildren.push({
            shape: childShape,
            bounds: {
              a: {
                x: childShape.bounds.a.x,
                y: childShape.bounds.a.y
              },
              b: {
                x: childShape.bounds.b.x,
                y: childShape.bounds.b.y
              }
            }
          })
        }.bind(this)
      )

      this.shapeUpperLeft = this.shape.bounds.upperLeft()

      // If there is no parent,
      // correct the abs position with the parents abs.
      /*if (!this.shape.parent) {
                        var pAbs = parent.absoluteXY();
                        this.shapeUpperLeft.x += pAbs.x;
                        this.shapeUpperLeft.y += pAbs.y;
                }*/
      this.parentHeight = this.parent.bounds.height()
    },

    getLeafLanes: function(lane) {
      var childLanes = this.plugin
        .getLanes(lane)
        .map(
          function(child) {
            return this.getLeafLanes(child)
          }.bind(this)
        )
        .flatten()
      return childLanes.length > 0 ? childLanes : [lane]
    },

    findNewLane: function() {
      var lanes = this.plugin.getLanes(this.parent)

      var leafLanes = this.getLeafLanes(this.parent)
      /*leafLanes = leafLanes.sort(function(a,b){
                        var aupl = a.absoluteXY().y;
                        var bupl = b.absoluteXY().y;
                        return aupl < bupl ? -1 : (aupl > bupl ? 1 : 0)
                })*/
      this.lane =
        leafLanes.find(
          function(l) {
            return l.bounds.upperLeft().y >= this.shapeUpperLeft.y
          }.bind(this)
        ) || leafLanes.last()
      this.laneUpperLeft = this.lane.bounds.upperLeft()
    },

    execute: function() {
      if (this.changes) {
        this.executeAgain()
        return
      }

      /*
       * Rescue all ChildShapes of the deleted
       * Shape into the lane that takes its
       * place
       */

      if (!this.lane) {
        this.findNewLane()
      }

      if (this.lane) {
        var laUpL = this.laneUpperLeft
        var shUpL = this.shapeUpperLeft

        var depthChange = this.plugin.getDepth(this.lane, this.parent) - 1

        this.changes = $H({})

        // Selected lane is BELOW the removed lane
        if (laUpL.y >= shUpL.y) {
          this.lane.getChildShapes().each(
            function(childShape) {
              /*
               * Cache the changes for rollback
               */
              if (!this.changes[childShape.getId()]) {
                this.changes[childShape.getId()] = this.computeChanges(
                  childShape,
                  this.lane,
                  this.lane,
                  this.shape.bounds.height()
                )
              }

              childShape.bounds.moveBy(0, this.shape.bounds.height())
            }.bind(this)
          )

          this.plugin.hashChildShapes(this.lane)

          this.shapeChildren.each(
            function(shapeChild) {
              shapeChild.shape.bounds.set(shapeChild.bounds)
              shapeChild.shape.bounds.moveBy(shUpL.x - 30 - depthChange * 30, 0)

              /*
               * Cache the changes for rollback
               */
              if (!this.changes[shapeChild.shape.getId()]) {
                this.changes[shapeChild.shape.getId()] = this.computeChanges(shapeChild.shape, this.shape, this.lane, 0)
              }

              this.lane.add(shapeChild.shape)
            }.bind(this)
          )

          this.lane.bounds.moveBy(0, shUpL.y - laUpL.y)

          // Selected lane is ABOVE the removed lane
        } else if (shUpL.y > laUpL.y) {
          this.shapeChildren.each(
            function(shapeChild) {
              shapeChild.shape.bounds.set(shapeChild.bounds)
              shapeChild.shape.bounds.moveBy(shUpL.x - 30 - depthChange * 30, this.lane.bounds.height())

              /*
               * Cache the changes for rollback
               */
              if (!this.changes[shapeChild.shape.getId()]) {
                this.changes[shapeChild.shape.getId()] = this.computeChanges(shapeChild.shape, this.shape, this.lane, 0)
              }

              this.lane.add(shapeChild.shape)
            }.bind(this)
          )
        }
      }

      /*
       * Adjust the height of the lanes
       */
      // Get the height values
      var oldHeight = this.lane.bounds.height()
      var newHeight =
        this.lane.length === 1 ? this.parentHeight : this.lane.bounds.height() + this.shape.bounds.height()

      // Set height
      this.setHeight(newHeight, oldHeight, this.parent, this.parentHeight, true)

      // Cache all sibling lanes
      //this.changes[this.shape.getId()] = this.computeChanges(this.shape, this.parent, this.parent, 0);
      this.plugin.getLanes(this.parent).each(
        function(childLane) {
          if (!this.changes[childLane.getId()] && childLane !== this.lane && childLane !== this.shape) {
            this.changes[childLane.getId()] = this.computeChanges(childLane, this.parent, this.parent, 0)
          }
        }.bind(this)
      )

      // Update
      this.update()
    },

    setHeight: function(newHeight, oldHeight, parent, parentHeight, store) {
      // Set heigh of the lane
      this.plugin.setDimensions(this.lane, this.lane.bounds.width(), newHeight)
      this.plugin.hashedBounds[this.pool.id][this.lane.id] = this.lane.absoluteBounds()

      // Adjust child lanes
      this.plugin.adjustHeight(this.plugin.getLanes(parent), this.lane)

      if (store === true) {
        // Store changes
        this.changes[this.shape.getId()] = this.computeChanges(this.shape, parent, parent, 0, oldHeight, newHeight)
      }

      // Set parents height
      this.plugin.setDimensions(parent, parent.bounds.width(), parentHeight)

      if (parent !== this.pool) {
        this.plugin.setDimensions(
          this.pool,
          this.pool.bounds.width(),
          this.pool.bounds.height() + (newHeight - oldHeight)
        )
      }
    },

    update: function() {
      // Hack to prevent the updating of the dockers
      this.plugin.hashedBounds[this.pool.id]['REMOVED'] = true
      // Update
      //this.facade.getCanvas().update();
    },

    rollback: function() {
      var laUpL = this.laneUpperLeft
      var shUpL = this.shapeUpperLeft

      this.changes.each(
        function(pair) {
          var parent = pair.value.oldParent
          var shape = pair.value.shape
          var parentHeight = pair.value.parentHeight
          var oldHeight = pair.value.oldHeight
          var newHeight = pair.value.newHeight

          // Move siblings
          if (
            shape
              .getStencil()
              .id()
              .endsWith('Lane')
          ) {
            shape.bounds.moveTo(pair.value.oldPosition)
          }

          // If lane
          if (oldHeight) {
            this.setHeight(oldHeight, newHeight, parent, parent.bounds.height() + (oldHeight - newHeight))
            if (laUpL.y >= shUpL.y) {
              this.lane.bounds.moveBy(0, this.shape.bounds.height() - 1)
            }
          } else {
            parent.add(shape)
            shape.bounds.moveTo(pair.value.oldPosition)
          }
        }.bind(this)
      )

      // Update
      //this.update();
    },

    executeAgain: function() {
      this.changes.each(
        function(pair) {
          var parent = pair.value.newParent
          var shape = pair.value.shape
          var newHeight = pair.value.newHeight
          var oldHeight = pair.value.oldHeight

          // If lane
          if (newHeight) {
            var laUpL = this.laneUpperLeft.y
            var shUpL = this.shapeUpperLeft.y

            if (laUpL >= shUpL) {
              this.lane.bounds.moveBy(0, shUpL - laUpL)
            }
            this.setHeight(newHeight, oldHeight, parent, parent.bounds.height() + (newHeight - oldHeight))
          } else {
            parent.add(shape)
            shape.bounds.moveTo(pair.value.newPosition)
          }
        }.bind(this)
      )

      // Update
      this.update()
    },

    computeChanges: function(shape, oldParent, parent, yOffset, oldHeight, newHeight) {
      oldParent = this.changes[shape.getId()] ? this.changes[shape.getId()].oldParent : oldParent
      var oldPosition = this.changes[shape.getId()] ? this.changes[shape.getId()].oldPosition : shape.bounds.upperLeft()

      var sUl = shape.bounds.upperLeft()

      var pos = { x: sUl.x, y: sUl.y + yOffset }

      var changes = {
        shape: shape,
        parentHeight: oldParent.bounds.height(),
        oldParent: oldParent,
        oldPosition: oldPosition,
        oldHeight: oldHeight,
        newParent: parent,
        newPosition: pos,
        newHeight: newHeight
      }

      return changes
    }
  })

  ORYX.Plugins.BPMN2_0 = ORYX.Plugins.AbstractPlugin.extend(ORYX.Plugins.BPMN2_0)
})()
